PanoPostprocessorService::run | resize pano id:92617986 | error {"stack":"HttpError: HTTP request fa
RCA: PanoPostprocessorService::run resize pano id:92617986 — 502 Bad Gateway
Overview#
What Happened#
2026-07-09 09:39:34 KST, cupixworks-pix-genie-preprocessor-instance (whitingturner tenant, us-west-2) 에서 pano id 92617986 의 resize 태스크가 tesla API 호출 중 nginx 로부터 502 Bad Gateway 를 받아 실패했습니다. Datadog 상 14일 내 동일 에러는 1건뿐인 일회성 이벤트이며, 해당 pano 는 Error state 로 마킹되었으나 capture 단위 postprocess 는 정상 종료되었습니다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | HttpError |
| exception.message | HTTP request failed |
| top_frame | panoApi.js:2020:40 (getPano 응답 처리) |
| runtime | Node.js (agent Docker image, @tesla/typescript-node-sdk@1.13.3-SNAPSHOT.202605110829) |
| env | production, us-west-2 |
| tenant | cupix / whitingturner |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| whitingturner (pano postprocess) | 1 | pano id 92617986 1건이 Error state 로 마킹, capture 자체는 Done 처리 |
Timeline#
- 2026-07-09 09:07:09 KST —
PanoPostprocessorService::run | begin(whitingturner capture 처리 시작) - 2026-07-09 09:38:45 KST —
POST /api/v1/panos/92617986/tile_upload_credentials200 (resize 태스크 진행 중) - 2026-07-09 09:38:59 KST —
PUT /api/v1/panos/92617986/check_tile_uploading200 (uploadResizedImage 완료) - 2026-07-09 ~09:39:03 KST —
ResizeWork.checkStitched내pano.get(92617986)최초 시도, nginx 가 502 반환 (추정: 마지막 성공 API 시각과 에러 로그 시각 간 ~35s 차이가 retry 지수 백오프(1+2+4+8+16=31s)와 부합) - 2026-07-09 09:39:34 KST —
cupixRetriableRequest5회 재시도 모두 502 →HttpError최종 reject → resize 태스크 catch 에서logger.error및updatePanoState(Error)실행 (첫 에러 발생) - 2026-07-09 09:39:49 KST —
PUT /api/v1/panos/92617986200 (updatePanoState성공, API/nginx 는 이미 회복) - 2026-07-09 09:49:02 KST —
PanoPostprocessorService::run | end(outer catch 미발생, capture postprocess state=Done)
Error Log#
PanoPostprocessorService::run | resize pano id:92617986 | error {"stack":"HttpError: HTTP request failed
at Request._callback (/tmp/agent/dist/node_modules/.pnpm/@tesla+typescript-node-sdk@1.13.3-SNAPSHOT.202605110829_c0aaa38c9a9fff396d4124c32a1f39fa/node_modules/@tesla/typescript-node-sdk/api/panoApi.js:2020:40)
at self.callback (/tmp/agent/dist/node_modules/.pnpm/request@2.88.2/node_modules/request/request.js:185:22)
at Request.emit (node:events:524:28)
...
","message":"HTTP request failed","response":{"body":"<html>\r
<head><title>502 Bad Gateway</title></head>\r
<body>\r
<center><h1>502 Bad Gateway</h1></center>\r
<hr><center>nginx</center>\r
</body>\r
</html>\r
","statusCode":502},"body":{},"statusCode":502,"name":"HttpError"}
Impact#
- Service:
cupixworks-pix-genie-preprocessor-instance - Team: whitingturner
- 발생 횟수: 1
- 최초 발생: 2026-07-09 09:39 KST
- 최근 발생: 2026-07-09 09:39 KST
pano 1건이 Error state 로 마킹되었으나, capture 단위 후처리 파이프라인은 outer try/catch 를 트리거하지 않고 정상 종료. 사용자 시점에서는 해당 capture 의 특정 pano 만 재처리 필요.
Root Cause Summary#
ResizeWork.checkStitched 에서 tesla API 의 GET /api/v1/panos/{id} 를 호출하는 도중 nginx 프록시가 502 Bad Gateway 를 반환했습니다. SDK 의 cupixRetriableRequest 는 502 를 retriable status 로 인식해 지수 백오프로 최대 5회 재시도(1s → 2s → 4s → 8s → 16s, 총 ~31s)를 수행했지만, 5회 모두 502 를 받았습니다. 결과적으로 HttpError 가 상위로 던져지고 resize 태스크의 catch 블록에서 pano state 가 Error 로 마킹되었습니다. Rails 백엔드 access log 에는 해당 요청이 5xx 로 남지 않아, 요청이 upstream(Rails pod)까지 도달하지 못하고 nginx-Rails 사이에서 실패한 upstream 오류로 판단됩니다.
동 fingerprint 로는 14일 내 1건이지만, nginx↔Rails 사이 5xx 자체는 이번 사건 시점(2026-07-09 00:28-01:03 UTC)에 여러 agent 서비스에서 동시다발적으로 발생한 incident 창의 일부입니다. 이 revision 에서 재조사한 결과, 같은 창에서 502(20건)와 504(10건)가 cupixworks-pano-postprocessor-instance, cupixworks-capture-postprocessor-agent, cupixworks-capture-preprocessor-agent, cupixworks-pix-genie-preprocessor-instance 등 여러 agent 서비스에서 관측되며, 같은 시간에 error-sweeper 의 active incident 2026-07-08-svc-cupixworks-api--resource-exhaustion-2 가 열려 있었습니다. 즉 본 클러스터는 순수한 일회성 upstream 실패가 아니라 Rails 백엔드의 resource exhaustion 이 nginx↔Rails 경로에서 502/504 로 나타난 창의 tail event 로 해석하는 것이 정확합니다. 다만 이 특정 fingerprint 는 pano id 92617986 이 SDK 의 5회 재시도까지 소진한 유일한 케이스라 재발 카운트가 1 로 잡힌 것입니다.
Technical Analysis#
Code Path#
- Entry point:
pano-postprocessor-service.ts:79(PanoPostprocessorService.run) - Resize 병렬 처리:
pano-postprocessor-service.ts:165-178에서 각 pano 별로resizePano → uploadResizedImage → checkStitched를 실행 - Failure point:
resize-work.ts:112—this.cupixApi.pano.get(cpPano.panoId)호출 중 502 반환 - Retry & error surface:
panoApi.js:2020에서reject(new HttpError(response, body, 502))→cupixRetriableRequest가 5회 재시도 후 최종 reject
const resizeTasks = cpPanos.map((cpPano) => {
return PARALLEL_TASK_LIMIT(async () => {
try {
await this.resizeWork.resizePano(cpPano);
await this.resizeWork.uploadResizedImage(cpPano);
await this.resizeWork.checkStitched(cpPano);
} catch (error) {
logger.error('PanoPostprocessorService::run | resize pano id:%d | error %s', cpPano.panoId, stringifyError(error));
await this.panoPostprocessorManager.updatePanoState(cpPano.panoId!, TESLA.UpdatePanoRequest.StateEnum.Error);
erroredPanoIds.add(cpPano.panoId!);
}
});
});
checkStitched = async (cpPano: CPPano): Promise<void> => {
if (!cpPano.panoId) return;
// Check if pano is already stitched
const pano = await this.cupixApi.pano.get(cpPano.panoId); // ← 502 발생 지점
if (pano.stitched) {
logger.debug('ResizeWork::checkStitched | pano %d is already stitched, skipping update ...');
return;
}
...
};
const requestPromise = () => new Promise((resolve, reject) => {
request_1.default(localVarRequestOptions, (error, response, body) => {
if (error) {
reject(error);
}
else {
body = models_1.ObjectSerializer.deserialize(body, "PanoResponse");
if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) {
resolve({ response: response, body: body });
}
else {
reject(new apis_1.HttpError(response, body, response.statusCode)); // ← line 2020, stack top
}
}
});
});
return this.cupixRetriableRequest(requestPromise);
private retriableStatusCodes = new Set<number>([
500, // Internal Server Error
502, // Bad Gateway
503, // Service Unavailable
504 // Gateway Timeout
]);
...
private cupixRetriableRequest = <T>(func: () => Promise<T>, retries?: number): Promise<T> => new Promise((resolve, reject) => {
const MaxRetries = 5;
const _retries = retries != undefined ? retries : 0;
const _retryInterval = Math.pow(2, _retries) * 1000;
func()
.then(response => resolve(response))
.catch(e => {
if (_retries < MaxRetries && e && this.isCupixRetriableRequest(e)) {
setTimeout(() => {
this.cupixRetriableRequest(func, _retries + 1).then(resolve).catch(reject);
}, _retryInterval);
} else {
reject(e);
}
});
});
기대 동작: 502 는 retriable 이므로 5회 재시도 안에 회복된 nginx/upstream 에서 200 을 받아 성공. 실제 동작: 5회 모두 502 → HttpError 상위 전파 → resize 태스크 catch → pano state=Error 마킹.
Log Evidence#
Datadog 쿼리 (재현용, cluster 파일 URL 그대로):
service:cupixworks-pix-genie-preprocessor-instance status:error @environment:production "PanoPostprocessorService::run"
같은 pano id 로 넓혀본 쿼리:
service:cupixworks-pix-genie-preprocessor-instance 92617986
핵심 로그 (KST):
2026-07-09 09:38:45 [200] POST /api/v1/panos/92617986/tile_upload_credentials
2026-07-09 09:38:59 [200] PUT /api/v1/panos/92617986/check_tile_uploading
2026-07-09 09:39:34 ERROR PanoPostprocessorService::run | resize pano id:92617986 | error ... statusCode:502
2026-07-09 09:39:49 [200] PUT /api/v1/panos/92617986 (updatePanoState=Error 반영)
14일 재발 검증:
service:cupixworks-pix-genie-preprocessor-instance "502 Bad Gateway"
→ Found 1 logs (해당 이벤트 1건만).
Rails 백엔드 access log 조회 (동시간대 cupixworks-api): 문제 요청에 해당하는 502/5xx access log 없음. 즉 요청이 Rails 프로세스까지 도달하지 못하고 nginx 상단에서 502 를 응답한 것으로 판단.
nginx↔Rails 5xx 최근 빈도 (revision 1 재조사, now-14d):
- 쿼리
"502 Bad Gateway" "nginx"— 총 70건. 그 중 51건은cupixworks-capture-zip-productionLambda(별도 fingerprint), 나머지 19건이 agent Node 서비스. - 쿼리
"HttpError" ("500" OR "502" OR "503" OR "504")— 총 39건 (502: 23, 504: 14, 503: 2, 500: 0). - 서비스별 502 (14일):
cupixworks-pano-postprocessor-instance16,cupixworks-capture-postprocessor-agent2,cupixworks-capture-intelligence-agent2,cupixworks-pix-genie-preprocessor-instance1(본 클러스터),cupixworks-capture-preprocessor-agent1,cupixworks-any-potree-agent1. - 일자별 5xx (HttpError 기준): 06-26 (504×1), 07-01 (504×1), 07-03 (502×2, 504×2), 07-06 (502×1), 07-07 (503×1), 07-08 (503×1), 07-09 (502×20, 504×10).
- 2026-07-09 세부 분포(UTC): 00:29-00:41 사이 502 20건 중 18건이 09:39(UTC 00:39) 창에 집중, 504 는 00:28-01:03 창에 분산.
service:(cupixworks-pano-postprocessor-instance OR cupixworks-pix-genie-preprocessor-instance OR cupixworks-capture-postprocessor-agent OR cupixworks-capture-preprocessor-agent) "HttpError" ("502" OR "504")
Status board 교차 확인: 동시간에 error-sweeper active incident 2026-07-08-svc-cupixworks-api--resource-exhaustion-2 (started 2026-07-08T12:33:46Z, last_event 2026-07-09T06:27:57Z) 가 열려 있음. Rails 백엔드 resource exhaustion 이 upstream 5xx 로 노출된 것으로 판단.
해석: nginx↔Rails 사이 5xx 는 평상시에는 하루 0-2건의 baseline. 이번 event 창(2026-07-09 00:28-01:03 UTC)에는 30분 안에 30건이 여러 agent 서비스에서 관측되어 baseline 대비 최소 10배 이상 spike. 본 클러스터는 그 spike 의 tail 에 속한 이벤트.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | nginx-upstream 사이의 순간적 upstream 실패 (pod 재시작, keepalive reset, worker timeout 등) | 응답 body 가 nginx 기본 502 HTML, cupixworks-api Rails 로그에 대응 요청 부재, 인접 요청은 200 |
동일 창(2026-07-09 00:28-01:03 UTC)에 5xx 30건이 여러 agent 서비스에서 관측 — 순간적이 아니라 지속된 upstream 실패 창 | Partial |
| H2 | tesla API 전역 장애 | 동일 시각(09:38-09:41) 다른 pano GET /panos/{id} 다수가 200 응답, cupixworks-api status:error 에도 관련 500 없음 |
다른 pano 호출 모두 성공 | Rejected |
| H6 | Rails 백엔드 resource exhaustion 로 인한 nginx 상단 5xx spike | Status board 상 2026-07-08-svc-cupixworks-api--resource-exhaustion-2 가 동시간에 active, 같은 창 5xx 가 여러 agent 서비스에서 30건 spike (baseline 대비 10배+) |
개별 요청에 대한 Rails access log 없음(nginx 상단에서 502) — direct code path 는 확인 불가 | Confirmed (revision 1) |
| H3 | SDK retry 미동작 | 재시도 총합 시간(~31s) 이 마지막 성공(09:38:59) ~ 에러 로그(09:39:34) 사이 35s 와 부합 | cupixRetriableRequest 코드 상 502 는 retriable set 에 포함, 정상 동작 |
Rejected |
| H4 | 애플리케이션 코드 버그 (예: pano id 오류, 파라미터 누락) | — | 동일 pano 가 앞뒤 API 호출(200)에서 정상 처리됨, 스택 최상단이 SDK 응답 dispatch 라인이며 클라이언트 검증 실패 아님 | Rejected |
| H5 | checkStitched catch 누락으로 노이즈성 실패 |
resize 태스크 catch 는 HttpError 도 무차별 catch → pano state=Error, checkStitched 내부는 updateStitched 실패만 warn 처리, pano.get 실패는 상위로 전파 |
이번 이벤트는 실제 upstream 실패라서 노이즈로 단정하기 어려움 | Inconclusive |
Fix Recommendation#
즉시 조치 (Critical)#
- 코드 변경 불필요. 다만 이 클러스터는 순수 일회성이 아니라 active incident
2026-07-08-svc-cupixworks-api--resource-exhaustion-2의 tail event 이므로 해당 incident 의 진행 상황을 우선 추적. 대상 pano92617986은 상위 incident 해결(또는 창 종료) 확인 후 재처리(재큐잉).
단기 개선 (1주 이내)#
applications/agents/packages/cupix-pano-postprocessor/src/work/resize-work.ts:108-132checkStitched관찰: 현재pano.get실패는 상위(catch → pano state=Error)로 전파되지만,updateStitched실패는 warn 만 남기고 무시. 정책 정합성을 위해pano.get실패도 warn 후 continue(=stitched 이미 처리로 간주하지 않고 skip) 로 다루는 것이 재발 시 영향 최소화에 유리한지 검토. 근거: 동일 SDK 가 이미 500/502/503/504 를 5회 재시도하므로 catch 시점의 실패는 대체로 pod 레벨 장애이고, 이 pano 만 error 로 격리하는 지금 동작이 오히려 안전할 수도 있음 — 팀 판단 필요.- pano id
92617986재처리 확인 후, 재처리 성공하면 클러스터를 무시 대상(운영 이벤트) 로 태깅.
장기 개선 (재발 방지)#
- nginx ↔ Rails(unicorn/puma) 사이 502 발생 원인 관측성 강화:
cupixworks-api서비스에 nginx access log(또는 ALB access log) 를 Datadog 으로 수집해 5xx 원인(upstream timeout, connection reset by peer, no live upstreams 등) 을 분류. 현재는 Rails 로그만 있어 nginx-only 실패가 관측 사각지대. - pano 단위 실패 재큐잉 자동화: 현재 pano state=Error 로 마킹 후에는 별도 재실행 파이프라인이 없어 보임 (
applications/agents/packages/cupix-pano-postprocessor전역 검색 시 재큐잉 로직 미검출). "SDK 재시도 소진 → 단일 pano error" 케이스가 반복될 경우 자동 재큐잉 워크플로우가 유리.
Monitoring#
- 추가할 메트릭/알림
cupixworks-pix-genie-preprocessor-instance서비스의 5xx-driven pano error 발생률- resize 태스크 실패율 (총 pano 대비 erroredPanoIds 비율) 이 임계치 초과 시 알림
Datadog timeseries widget 쿼리 예시:
count:service:cupixworks-pix-genie-preprocessor-instance status:error "resize pano id"
count:service:cupixworks-pix-genie-preprocessor-instance status:error "502 Bad Gateway"
count:service:cupixworks-pix-genie-preprocessor-instance status:error "PanoPostprocessorService::run"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (재처리만 필요, 코드 변경 불필요)
Revision History#
Revision 1#
Feedback: 최근 nginx ↔ Rails 사이 에러가 얼마나 발생하는지 추가 조사.
판정:
| 피드백 항목 | 판정 | 근거 |
|---|---|---|
| nginx↔Rails 사이 5xx 최근 빈도 재조사 | 수용 | Datadog 쿼리 "502 Bad Gateway" "nginx" (now-14d) → 70건 (Lambda 51 + agent Node 19). "HttpError" ("500" OR "502" OR "503" OR "504") → 39건 (502: 23, 504: 14, 503: 2). 일자별: 06-26/07-01/07-03/07-06/07-07/07-08 각 1-4건 baseline, 07-09 30건 spike. 07-09 UTC 00:28-01:03 창에 cupixworks-pano-postprocessor-instance, cupixworks-capture-postprocessor-agent, cupixworks-capture-preprocessor-agent, cupixworks-pix-genie-preprocessor-instance, cupixworks-capture-intelligence-agent, cupixworks-any-potree-agent 6개 agent 서비스에서 동시다발 발생. |
| 기존 "일회성 이벤트" 프레이밍 재검토 | 부분 수용 | 동일 fingerprint 는 여전히 14일 내 1건이 맞음 (GET /api/v1/panos/92617986 재시도 소진 사례는 pano 92617986 하나뿐). 그러나 nginx↔Rails 5xx 자체는 일회성이 아니라 30분 창의 spike 이므로 상위 Root Cause 문구를 "일회성 upstream 실패" → "upstream 5xx spike 창의 tail event" 로 수정. Status board 상 2026-07-08-svc-cupixworks-api--resource-exhaustion-2 (open, started 2026-07-08T12:33:46Z, last_event 2026-07-09T06:27:57Z) 가 동시간에 active — Rails 백엔드 resource exhaustion 이 원인일 가능성이 높음. |
변경 사항:
## Root Cause Summary수정: "일회성 upstream 오류" 표현을 유지하되, 뒤에 nginx↔Rails 5xx spike 창(2026-07-09 00:28-01:03 UTC) 과 active incidentsvc:cupixworks-api::resource_exhaustion상관관계 문단 추가.### Log Evidence에 "nginx↔Rails 5xx 최근 빈도" 소절 추가 — 쿼리, 서비스별 카운트, 일자별 카운트, status board 교차 확인, baseline 대비 spike 배수 명시.## Hypotheses Considered— H1 verdict 를 Confirmed → Partial 로 downgrade. H6 (Rails 백엔드 resource exhaustion) 를 Confirmed 로 추가.## Fix Recommendation즉시 조치 문구를 "1회성" → "active incident tail event" 로 수정하고, incident 진행 추적 우선 지시.
추가 조사 내용:
- Datadog 쿼리 실행:
"502 Bad Gateway" "nginx"(now-14d, 70건),"HttpError" ("500" OR "502" OR "503" OR "504")(now-14d, 39건). status-boardskill 로 active/recent incident 확인 →2026-07-08-svc-cupixworks-api--resource-exhaustion-2(open) 매치.- 서비스별/시간대별 aggregation 을 통해 baseline (0-2건/day) 대비 spike (30건/30min) 확인.