ES /docs

ImageCPObject::putThumbnail | Not json format -

RCA: ImageCPObject::putThumbnail | Not json format

Error Log#

Datadog Logs

text
ImageCPObject::putThumbnail | Not json format -

Impact#

  • Service: cupixworks-any-thumbnail-agent
  • Team: bnbuilders
  • 발생 횟수: 1
  • 최초 발생: 2026-04-13T19:16:35.709Z
  • 최근 발생: 2026-04-13T19:16:35.709Z

Root Cause Summary#

Thumbnail agent가 Asset jswa50t20cqx의 thumbnail을 업로드하기 위해 Tesla API(PUT /api/v1/assets/jswa50t20cqx)를 호출했으나, Tesla API 내부에서 CarrierWave/Fog를 통해 S3 bucket cupix-tesla-static에 파일을 업로드할 때 AWS S3 region 불일치 오류(AuthorizationHeaderMalformed: the region 'us-east-1' is wrong; expecting 'us-west-2')가 발생했다. 이로 인해 Tesla API가 HTTP 500 응답을 반환했고, thumbnail agent는 non-JSON 응답 body를 받아 "Not json format" 에러를 로깅했다. 핵심 원인은 CarrierWave의 fog_credentials에 region이 올바르게 전달되지 않아 AWS SDK 기본값(us-east-1)이 사용된 것이다.

Technical Analysis#

Code Path#

  • Entry point (thumbnail agent): thumbnail-service.ts:79updateThumbnail() 호출
  • Thumbnail agent는 thumbnail 생성 후 updateThumbnail()을 호출하여 Tesla API에 PUT 요청을 보냄
typescript
// cupixworks/applications/agents/packages/cupix-tesla-thumbnail-agent/src/thumbnail-service.ts:77-80
await supportModel.downloadOriginal();
await supportModel.preprocess();
await supportModel.generateThumbnail();
await supportModel.updateThumbnail();  // <-- 여기서 putThumbnail 호출
  • updateThumbnail()putThumbnail()을 호출하며, 이 메서드는 Tesla API endpoint(/api/v1/assets/{key})에 thumbnail 파일을 multipart form으로 PUT 전송
typescript
// cupixworks/applications/agents/packages/cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:120-162
private putThumbnail = (url: string, filePath: string): Promise<T | undefined> => new Promise((resolve, reject) => {
    // ...
    request.put(url, options, (error, response, body) => {
        if (error) {
            reject(cupixAuth.handleError(error));
        } else {
            if (CPUtils.isJsonString(body)) {
                const res = JSON.parse(body);
                // ... 성공 처리
            } else {
                logger.error('ImageCPObject::putThumbnail | Not json format - %s', body);
                resolve(undefined);  // <-- 에러이지만 resolve(undefined)로 처리
            }
        }
    });
});
  • Failure point (Tesla API): Api::V1::AssetsController#update — CarrierWave가 S3에 thumbnail을 업로드하려 할 때, S3가 region 불일치로 400 Bad Request 반환

  • Tesla API 측에서 CarrierWave 초기화 시 fog_credentials에 region이 설정되지 않아 AWS SDK 기본값인 us-east-1이 사용됨

ruby
# tesla/config/initializers/carrierwave.rb:1-11
Rails.application.config.to_prepare do
  CarrierWave.configure do |config|
    config.fog_credentials = {
      provider: 'AWS',
      aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'] || ...,
      aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] || ...,
      path_style: true
      # region이 설정되지 않음 — AWS SDK default는 us-east-1
    }
    config.fog_directory = $AWS[:s3][:static_bucket_name]
  end
end
  • ThumbnailUploader는 model의 fog_credentials를 merge하지만, 이 merge가 정상적으로 region을 포함해야 함
ruby
# tesla/app/uploaders/thumbnail_uploader.rb:13-17
def initialize(*)
  super
  self.asset_host = model.asset_host
  self.fog_credentials.merge!(model.fog_credentials)  # model에서 region 가져옴
  self.fog_directory = model.fog_directory
end
  • V2 thumbnail의 경우 $AWS[:s3][:static_bucket_region] (us-west-2)을 사용하나, V1 또는 merge 실패 시 기본 region(us-east-1)이 그대로 사용됨
ruby
# tesla/app/models/concerns/thumbnailable/v2.rb:5-15
def v2_settings
  {
    asset_host: "https://#{$AWS[:cloudfront][:static_hostname]}",
    fog_credentials: {
      region: $AWS[:s3][:static_bucket_region],  # us-west-2
      endpoint: v2_s3_endpoint
    },
    fog_directory: $AWS[:s3][:static_bucket_name],
    thumbnail_path: "..."
  }
end
  • 기대 동작: fog_credentialsregion: 'us-west-2'가 설정되어 S3 PUT 성공 → JSON 응답 반환
  • 실제 동작: region이 us-east-1로 설정되어 S3 PUT 실패 → API가 500 응답 → agent가 non-JSON body 수신

Log Evidence#

Thumbnail agent 측 실행 흐름 (asset jswa50t20cqx):

text
Datadog query: service:cupixworks-any-thumbnail-agent "jswa50t20cqx"
Time range: 2026-04-13T19:10:00Z to 2026-04-13T19:25:00Z
text
2026-04-13T19:16:08.638Z [info] ImageCPObject::downloadOriginal | url: http://api-tesla.cupix.internal/api/v1/assets/jswa50t20cqx/download, path: /tmp/workspace/jswa50t20cqx/jswa50t20cqx.jpg
2026-04-13T19:16:10.572Z [info] ImageCPObject::generateThumbnail | params: {"input_file_path":"/tmp/workspace/jswa50t20cqx/jswa50t20cqx.jpg","output_image_path":"/tmp/workspace/jswa50t20cqx/jswa50t20cqx_thumbnail.jpg","resized_width":500,"resized_height":500,"frame":0}
2026-04-13T19:16:16.427Z [info] ImageCPObject::generateThumbnail | end - output: /tmp/workspace/jswa50t20cqx/jswa50t20cqx_thumbnail.jpg, size: 17524, elapsed time: 5855 ms
2026-04-13T19:16:35.709Z [error] ImageCPObject::putThumbnail | Not json format -
2026-04-13T19:16:35.710Z [info] ImageCPObject::downloadOriginal | url: http://api-tesla.cupix.internal/api/v1/assets/jswa50t20cqx/download (cover 단계로 진행)
2026-04-13T19:16:37.923Z [info] ImageCPObject::generateCover | end - output: /tmp/workspace/jswa50t20cqx/jswa50t20cqx_cover.jpg, size: 633022, elapsed time: 2212 ms
2026-04-13T19:16:38.994Z [info] BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/jswa50t20cqx

Tesla API 측 로그 (asset jswa50t20cqx):

text
Datadog query: service:cupixworks-api "jswa50t20cqx"
Time range: 2026-04-13T19:15:00Z to 2026-04-13T19:20:00Z
text
2026-04-13T19:16:08Z [info] [200] GET /api/v1/assets/jswa50t20cqx (Api::V1::AssetsController#show)
2026-04-13T19:16:10Z [info] [302] GET /api/v1/assets/jswa50t20cqx/download (Api::V1::AssetsController#download)
2026-04-13T19:16:36Z [info] [500] PUT /api/v1/assets/jswa50t20cqx (Api::V1::AssetsController#update)

S3 에러 세부 내용 (Tesla API 500 응답에 포함):

text
Excon::Error::BadRequest
Expected(200) <=> Actual(400 Bad Request)
Body: "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<Error>
  <Code>AuthorizationHeaderMalformed</Code>
  <Message>The authorization header is malformed; the region 'us-east-1' is wrong; expecting 'us-west-2'</Message>
  <Region>us-west-2</Region>
</Error>"
Host: cupix-tesla-static.s3.amazonaws.com
Path: /cupix-tesla-static/cupix-tesla-static/uploads/tmp/.../jswa50t20cqx_thumbnail.jpg

주목할 점:

  • S3 path에 cupix-tesla-static/cupix-tesla-static/uploads/tmp/...로 bucket 이름이 중복 — path_style: true 설정과 CarrierWave의 fog directory 설정이 충돌하여 path가 이중으로 생성된 것으로 추정
  • S3 region이 us-east-1로 전송되었으나 bucket은 us-west-2에 위치
  • Cover 업로드(cover_upload_url, check_cover_uploading)는 정상 동작 (200) — cover는 presigned URL 방식으로 직접 S3에 업로드하므로 CarrierWave를 거치지 않음

에러 발생 빈도:

text
Datadog query: service:cupixworks-any-thumbnail-agent "Not json format"
Time range: now-14d

14일간 1건만 발생 — 간헐적 현상으로, 특정 asset의 thumbnail_version 또는 storage_option 설정에 따라 발생할 수 있음.

Fix Recommendation#

즉시 조치 (Critical)#

  • tesla/config/initializers/carrierwave.rb:3-8: CarrierWave 기본 fog_credentialsregion: $AWS[:s3][:static_bucket_region]을 추가하여, model-level merge가 실패하더라도 올바른 region이 사용되도록 fallback 설정
  • tesla/app/uploaders/thumbnail_uploader.rb:16-17: fog_credentials.merge! 후 region이 올바르게 설정되었는지 로깅 추가

단기 개선 (1주 이내)#

  • S3 path에 bucket 이름이 중복되는 문제 (cupix-tesla-static/cupix-tesla-static/...) 조사 — path_style: true 설정과 fog directory 설정의 상호작용 확인 필요
  • Thumbnail agent의 putThumbnail 에러 핸들링 개선: non-JSON body일 때 resolve(undefined) 대신 response status code를 확인하고, 500 에러는 명확하게 에러로 처리하여 retry 또는 알림 가능하도록 변경 (image-cpobject.ts:142-154)

장기 개선 (재발 방지)#

  • CarrierWave/Fog 업로드 시 S3 region 설정을 중앙에서 관리하여 model별 override에 의존하지 않도록 아키텍처 개선
  • Thumbnail upload 실패 시 agent 레벨에서 재시도 로직 추가 (현재는 에러를 무시하고 undefined로 resolve)

Monitoring#

  • Thumbnail upload 실패 알림 추가:
text
service:cupixworks-any-thumbnail-agent status:error "putThumbnail"
  • Tesla API S3 region 오류 모니터링:
text
service:cupixworks-api status:error "AuthorizationHeaderMalformed"

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: standard — CarrierWave 초기화 설정 변경 + agent error handling 개선 필요