ImageCPObject::downloadFile | response path: /tmp/workspace/zkv5d75d9mwy/zkv5d75d9mwy.jpg, code: 504
RCA: ImageCPObject::downloadFile 504 Gateway Time-out
Overview#
What Happened#
2026-07-11 02:12 KST 무렵 cupixworks-any-thumbnail-agent (us-west-2, production) 에서 원본 이미지 파일을 tesla API 로부터 내려받는 도중 nginx 가 504 Gateway Time-out 을 반환하여 두 건의 asset 썸네일 생성 작업이 실패했다. 실패한 요청은 http://api-tesla.cupix.internal/api/v1/assets/{key}/download 이며, 두 workspace (zkv5d75d9mwy, 3cmc03w8qhqf) 모두 원본 다운로드 단계에서 실패한 뒤 workspace 가 정리되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | ImageCPObject::downloadFile |
| exception.message | response path: /tmp/workspace/{workspace}/{workspace}.jpg, code: 504, message: Gateway Time-out |
| top_frame | packages/cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:76 |
| runtime | Node.js SQS worker (thumbnail agent) |
| env | production, us-west-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| maryl / thumbnail agent | 2 | Asset 2건의 썸네일 생성이 원본 다운로드 단계에서 실패. SQS 메시지는 undefined response 로 판정되어 즉시 삭제되므로 재시도 없이 손실. |
Timeline#
- 2026-07-11 02:11:00 KST —
ImageCPObject::downloadOriginal | url: http://api-tesla.cupix.internal/api/v1/assets/zkv5d75d9mwy/download(info) — 다운로드 시작 - 2026-07-11 02:12:00 KST —
ImageCPObject::downloadFile | response ... code: 504, message: Gateway Time-out(error) — 첫 번째 asset 실패 - 2026-07-11 02:12:00 KST —
BaseService::handlingMessageErrors | Error and message object - {"error":"undefined response", ...}(error) — 메시지 삭제 처리 - 2026-07-11 02:11:11 KST —
ImageCPObject::downloadOriginal | url: .../3cmc03w8qhqf/download(info) — 두 번째 다운로드 시작 - 2026-07-11 02:12:11 KST — 동일 오류로 두 번째 asset 실패
Error Log#
ImageCPObject::downloadFile | response path: /tmp/workspace/zkv5d75d9mwy/zkv5d75d9mwy.jpg, code: 504, message: Gateway Time-out
Impact#
- Service:
cupixworks-any-thumbnail-agent - Team: maryl
- 발생 횟수: 2
- 최초 발생: 2026-07-11 02:12 KST
- 최근 발생: 2026-07-11 02:12 KST
Root Cause Summary#
ImageCPObject::downloadFile 이 tesla 내부 API (http://api-tesla.cupix.internal/api/v1/assets/{key}/download) 로 원본 이미지를 요청했을 때 앞단 nginx 가 upstream 응답을 기다리다 timeout 되어 HTTP 504 Gateway Time-out 을 반환했다. 이는 thumbnail agent 자체의 논리 버그가 아니라 업스트림 tesla API 의 asset download endpoint 응답 지연 이 근본 원인이다. 다만 agent 쪽에는 getApiErrorToDeleteMessage 가 5xx 응답(response 없이 res 객체가 그대로 reject 됨)을 "undefined response" 로 판정해 재시도 없이 SQS 메시지를 삭제 하는 동작이 있어, 일시적 gateway timeout 이 곧바로 asset 별 영구 실패로 이어졌다.
Technical Analysis#
Code Path#
- Entry point:
packages/cupix-tesla-thumbnail-agent/src/thumbnail-service.ts:58(ThumbnailService.run) — SQS 메시지 수신 후 asset 모델 초기화 - Step:
packages/cupix-tesla-thumbnail-agent/src/thumbnail-service.ts:77(supportModel.downloadOriginal()) - Step:
packages/cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:164-181(ImageCPObject.downloadOriginal→downloadFile) - Failure point:
packages/cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:74-77(request.get(...).on('response')분기, statusCode !== 200 이면 error 로그 후 reject) - Downstream error handling:
packages/base/src/base-service.ts:240-275(getApiErrorToDeleteMessage),packages/base/src/base-service.ts:290-313(handlingMessageErrors)
sendReq
.on('response', res => {
if (res.statusCode !== 200) {
logger.error('ImageCPObject::downloadFile | response path: %s, code: %d, message: %s', filePath, res.statusCode, res.statusMessage);
reject(cupixAuth.handleError(res));
} else {
sendReq.pipe(fileStream);
}
})
.on('error', err => {
logger.error('ImageCPObject::downloadFile | path: %s, error: %s', filePath, JSON.stringify(err));
reject(cupixAuth.handleError(err));
});
에이전트는 tesla API 로 원본 이미지를 GET 하고, upstream 이 200 이 아니면 즉시 reject 한다. 여기서 res 는 raw http.IncomingMessage 이며 res.response 는 존재하지 않는다.
private getApiErrorToDeleteMessage = (error: any): any => {
if (error == undefined) { ... return 'undefined error'; }
if (error.errno != undefined && error.code != undefined && error.syscall != undefined) {
logger.warn('BaseService::getApiErrorToDeleteMessage | nodejs common system error', error);
return;
}
const response = CPUtils.isJsonString(error) ? JSON.parse(error) : error.response;
if (response == undefined) {
logger.warn('BaseService::getApiErrorToDeleteMessage | undefined response', error);
return 'undefined response';
}
const statusCode = response.statusCode ? Number(response.statusCode) : undefined;
// ...
if (statusCode != undefined && statusCode >= 400 && statusCode <= 500) {
if (statusCode === 401) return;
return errorMsg;
}
return;
};
기대 동작: 5xx (gateway timeout 포함) 은 일시적일 수 있으므로 SQS 메시지를 그대로 두어 SQS 의 재전송 및 MaxReceiveCount 정책에 맡긴다.
실제 동작: handleError 가 reject 인자로 넘긴 raw response 는 error.response 필드가 없어 undefined response 분기로 떨어진다. getApiErrorToDeleteMessage 는 truthy 문자열 "undefined response" 를 반환하고, handlingMessageErrors (base-service.ts:301) 은 이 값을 "삭제 대상" 으로 간주해 deleteByMessage 를 즉시 호출한다. 결과적으로 504 로 실패한 메시지는 재시도 없이 큐에서 제거된다.
Log Evidence#
Datadog query used:
service:cupixworks-any-thumbnail-agent (zkv5d75d9mwy OR 3cmc03w8qhqf)
첫 번째 asset (zkv5d75d9mwy) 의 이벤트 시퀀스:
2026-07-11 02:11:00 info ImageCPObject::downloadOriginal | url: http://api-tesla.cupix.internal/api/v1/assets/zkv5d75d9mwy/download, path: /tmp/workspace/zkv5d75d9mwy/zkv5d75d9mwy.jpg
2026-07-11 02:12:00 error ImageCPObject::downloadFile | response path: /tmp/workspace/zkv5d75d9mwy/zkv5d75d9mwy.jpg, code: 504, message: Gateway Time-out
2026-07-11 02:12:00 error BaseService::handlingMessageErrors | Error and message object - {"error":"undefined response","sqsMessage":{"MessageId":"bf9bd366-fb98-42b8-9959-9335f2b1196b","Attributes":{"ApproximateReceiveCount":"1"}}}
2026-07-11 02:12:00 info BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/zkv5d75d9mwy
두 번째 asset (3cmc03w8qhqf) 는 11 초 후 완전히 동일한 시퀀스로 실패했다 (MessageId: 3e79e753-2234-4ea0-96b8-2f416e97f210, ApproximateReceiveCount: 1). 두 경우 모두 첫 시도(retry 0)에서 삭제됐다.
같은 시간대에 다른 형태의 nginx 504 도 다수 관찰돼 upstream 지연이 지속되고 있었음을 뒷받침한다:
2026-07-11 02:19:44 error ImageCPObject::putThumbnail | Not json format - <html>
<head><title>504 Gateway Time-out</title></head>
<body>
<center><h1>504 Gateway Time-out</h1></center>
<hr><center>nginx</center>
</body>
</html>
putThumbnail 응답 본문에 nginx 의 표준 504 HTML 이 그대로 담겨 있어 timeout 이 tesla 앞단 nginx 에서 발생했음을 확정할 수 있다. Datadog query:
service:cupixworks-any-thumbnail-agent status:error
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Tesla API (/api/v1/assets/{key}/download) 앞단 nginx 가 upstream timeout 으로 504 를 반환했다 |
`ImageCPObject::putThumbnail | Not json format - ...504 Gateway Time-out...로그 (2026-07-11 02:19:44 KST 등 다수), 실패 URL 이http://api-tesla.cupix.internal/api/v1/assets/{key}/download` 로 tesla 앞단, first_seen~last_seen 이 1분 이내 클러스터링 |
— |
| H2 | Thumbnail agent 의 request timeout 설정이 짧아 자체적으로 abort 한 것 | — | 에러 메시지 텍스트 "Gateway Time-out" 와 statusCode 504 는 서버가 응답한 값이며, 클라이언트 abort 이면 .on('error') 분기로 들어가 다른 로그 형태가 되었어야 한다 (image-cpobject.ts:82-85) |
Rejected |
| H3 | Asset 이 없거나 권한이 없어 tesla API 가 4xx 을 반환했다 | — | 응답 코드가 504 이며, 다른 asset 관련 오류(ENT4000 Bim not found) 와는 별개 시각에 발생 (02:27:30 KST). 클러스터 대상 asset key 는 존재 가정으로 다운로드 시작 로그(info) 가 남아있음 |
Rejected |
| H4 | 5xx 인 일시적 오류인데도 agent 가 SQS 메시지를 즉시 삭제해 재시도 기회를 잃는 부가 버그가 존재 | handlingMessageErrors 로그의 "error":"undefined response" (base-service.ts:250-252), ApproximateReceiveCount: 1 이 두 asset 모두 남아있음, getApiErrorToDeleteMessage 가 "undefined response" 문자열을 반환 → truthy → deleteByMessage 호출 경로 (base-service.ts:301-304) |
— | Confirmed (aggravating factor) |
Fix Recommendation#
즉시 조치 (Critical)#
- 없음 (외부 원인): 근본 원인은 tesla API 앞단 nginx 의 upstream timeout. 인프라/tesla 팀에
api-tesla.cupix.internal의/api/v1/assets/{key}/download응답 지연 원인 확인 요청. 발생 시점 (2026-07-11 02:12 KST, us-west-2) 의 tesla API, DB, S3 latency 를 함께 조사해야 한다.
단기 개선 (1주 이내)#
packages/base/src/base-service.ts:240-275getApiErrorToDeleteMessage의 5xx 처리 명시화: 현재undefined response분기에서 문자열을 반환해 삭제 대상으로 오인식되는 흐름이 문제. 아래 두 방향 중 하나로 보정 필요.image-cpobject.ts:77에서cupixAuth.handleError(res)대신{ response: res }형태의 정형화된 error 객체로 reject 하도록 조정하여 downstream 이 statusCode 를 볼 수 있게 한다.- 또는
getApiErrorToDeleteMessage에서error.statusCode를 직접 확인하는 폴백 분기를 추가해 5xx 는return;(삭제 대상 아님) 처리한다.
image-cpobject.ts:53-91downloadFile 에 요청 타임아웃/재시도:request.get에timeout옵션을 명시하고, 5xx 응답은 지수 백오프로 N 회 재시도 후 최종 실패시 reject.packages/api/src/authentication/cupix-auth.ts:59의retryable헬퍼가 존재하므로 이를 활용하는 방향으로 조정.
장기 개선 (재발 방지)#
- tesla API asset download endpoint 의 SLI/SLO 정의 및 알림: p95 latency, 5xx 비율에 대한 SLO 를 정의하고 상시 모니터링. 지금은 downstream 에서 개별 오류로 흩어져 upstream degradation 이 지연 감지된다.
- SQS 메시지 삭제 정책 리팩터:
undefined response문자열 반환으로 삭제 여부를 결정하는 관용구는 재발 위험이 크다. 삭제 대상은 statusCode 를 명시적으로 검사(4xx 만) 하도록 API 를 재설계. - Nginx upstream timeout 완화 또는 다운로드 endpoint 를 S3 presigned URL 직접 리다이렉트로 전환: 이미지 다운로드가 nginx 를 통과할 필요가 없도록 아키텍처 검토.
Monitoring#
Datadog dashboard/timeseries widget 용 쿼리 (release dashboard 로 그대로 붙일 수 있는 형태):
- Thumbnail agent 의 downloadFile 실패 (모든 statusCode):
service:cupixworks-any-thumbnail-agent status:error "ImageCPObject::downloadFile"
- Thumbnail agent 관련 504 전체 (downloadFile + putThumbnail nginx HTML 응답 포함):
service:cupixworks-any-thumbnail-agent status:error "504 Gateway Time-out"
- 같은 근본 원인의 tesla asset download 5xx 응답 (tesla API 측):
service:cupixworks-api status:error @http.url_details.path:"/api/v1/assets/*/download" @http.status_code:[500 TO 599]
- SQS 메시지가 5xx 로 즉시 삭제되는 케이스 추적 (aggravating factor):
service:cupixworks-any-thumbnail-agent status:error "handlingMessageErrors" "undefined response"
Risk Assessment#
- Risk level: medium — 개별 asset 썸네일 유실은 사용자 체감 (missing thumbnail) 로 이어지지만, 시스템 전체 장애는 아니며 발생 빈도가 낮다(관측 기간 내 2건). 다만 aggravating factor 인 "5xx 즉시 삭제" 는 향후 tesla 지연 시 대량 유실 위험이 있어 우선 완화 대상.
- 예상 복잡도: standard — 근본 원인 조사는 tesla/infra 협조 필요 (외부 조율). 코드 측 개선(재시도 + 삭제 정책 보정) 은 pure-code 로 처리 가능하나 SQS 재시도 동작 변경이 다른 agent 에도 영향을 주므로 회귀 테스트 필요.