No Captures found
RCA: No Captures found
Overview#
What Happened#
2026-07-09 17:11 KST 에 cupixworks-sitetrack-preprocessor-agent (us-west-2, production) 가 sitetrack id=20985, job id=1183964 처리 중 Error: No Captures found 를 던지고 job 을 실패 처리 (error code AGT1802) 했다. 원인은 sitetrack 대상 record id=137022 에 딸린 유일한 capture id=731776 의 published_at 이 null 이어서, getSrvCaptures 필터 체인의 .filter(capture => capture.published_at) 조건을 통과하지 못해 사용 가능한 video capture 가 0개가 된 것이다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Error |
| exception.message | No Captures found |
| top_frame | SiteTrackPreprocessorRunner.createTargetModels (sitetrack-preprocessor-runner.ts:16) |
| runtime | Node.js agent (/tmp/agent/dist/app.cjs) |
| error_code | AGT1802 (DeviationCapturesNotFound) |
| env | production, us-west-2 |
| host | ip-10-1-162-9.us-west-2.compute.internal |
| sitetrack.id | 20985 |
| job.id | 1183964 |
| record.id | 137022 |
| user | juhan121.lee@secc.co.kr (id 50180) |
| tenant | cupix |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| secc (production) | 1 | 단일 sitetrack job 1183964 실패 — 해당 사용자만 영향 |
| algtest (dev) | 다수 (2026-07-06 burst) | 개발 환경 batch 재실행 시 다수 sitetrack 이 동일 원인으로 실패 (production 영향 없음) |
Timeline#
- 2026-07-09 17:11:50.156 KST — Preprocessor agent 가 SQS 메시지 수신, job 1183964 로딩 시작 (session
295fac4cbfecce9fd33646184589507a6946aadf). - 2026-07-09 17:11:50.398 KST —
PreprocessorService::createCPSitetrack | begin - sitetrackId: 20985. - 2026-07-09 17:11:50.561 KST —
BasePreprocessorRunner::createCPBims | end - bim count: 5(BIM 은 정상 로드). - 2026-07-09 17:11:50.613 KST —
getSrvCaptures | load server Captures - count: 1, record id: 137022(record 에 capture 1개 존재). - 2026-07-09 17:11:50.614 KST —
getSrvCaptures | id: 731776, state: done, error_code: null, material: video, expected_quality: dollhouse, published_at: null—published_at: null로 인해 필터 탈락. - 2026-07-09 17:11:50.614 KST —
getSrvCaptures | filer out video Captures - count: 0→createCPCaptures | end. - 2026-07-09 17:11:50.616 KST —
Error: No Captures foundthrow,JobManager::updateErrorActionJob | begin - errorCode: AGT1802. - 2026-07-09 17:11:50.765 KST — SQS 메시지 삭제, job 종료.
Error Log#
No Captures found
스택 트레이스 원본:
Error: No Captures found
at SiteTrackPreprocessorRunner.createTargetModels (/tmp/agent/dist/app.cjs:9565:13)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async /tmp/agent/dist/app.cjs:820:24
at async descriptor.value (/tmp/agent/dist/app.cjs:819:30)
at async PreprocessorService.run (/tmp/agent/dist/app.cjs:9943:13)
at async /tmp/agent/dist/app.cjs:6159:17
at async BaseService2.runByMessage (/tmp/agent/dist/app.cjs:6155:15)
at async BaseService2.runByMessages (/tmp/agent/dist/app.cjs:6143:13)
at async BaseService2.checkingQueue (/tmp/agent/dist/app.cjs:6107:15)
at async PreprocessorService.init (/tmp/agent/dist/app.cjs:6334:9)
Impact#
- Service:
cupixworks-sitetrack-preprocessor-agent - Team: secc
- 발생 횟수: 1 (production, us-west-2)
- 최초 발생: 2026-07-09 17:11 KST
- 최근 발생: 2026-07-09 17:11 KST
- 부수 영향: 지난 7일 dev(us-west-2) 환경에서 동일 메시지가 100건 이상 (2026-07-06 12:58 KST burst 포함) 발생. production burst 는 없음. 사용자 단발성 실패이나 UX 관점에서 "capture 아직 publish 안 됨" 이라는 안내가 아니라 red-alert 형태 job failure 로 노출된다.
Root Cause Summary#
SiteTrackPreprocessorRunner.createTargetModels 는 TSLA-12556 hotfix (commit 9ee74a062, 2026-05-07) 로 추가된 if (cpCaptures.length === 0) throw new Error('No Captures found') guard 를 가지고 있다. createCPCaptures 는 BaseSiteinsightsPreprocessorRunner.getSrvCaptures 의 필터 체인을 통과한 capture 만 반환하는데, 그 마지막 필터가 .filter(capture => capture.published_at) 이다. 이번 사건에서는 sitetrack 20985 가 참조하는 record 137022 에 capture 가 딱 1개 (id 731776) 있었고 이 capture 는 state=done / material=video / expected_quality=dollhouse 를 만족했지만 published_at=null — 즉 아직 publish 되지 않은 capture 였다. 사용자가 publish 완료 이전에 sitetrack job 을 트리거했기 때문에 필터를 통과한 capture 가 0개가 되고, guard 가 error 를 던져 error_code: AGT1802 (DeviationCapturesNotFound) 로 job 이 실패했다. 이는 코드 로직상 정상적으로 감지된 user input precondition violation 이지만, error 레벨 로그로 기록되면서 monitoring 관점에서 실제 시스템 오류처럼 취급된다.
Technical Analysis#
Code Path#
- Entry point (SQS worker):
applications/agents/packages/cupix-sitetrack-preprocessor-agent/src/preprocessor-service.ts:49(PreprocessorService.run) - Runner invocation:
preprocessor-service.ts:71(await runner.createTargetModels(cpSitetrack)) - Failure point (throw):
applications/agents/packages/cupix-sitetrack-preprocessor-agent/src/runner/sitetrack-preprocessor-runner.ts:16-19(productionmaster) - 필터 로직:
applications/agents/packages/siteinsights/src/runner/base-siteinsights-preprocessor-runner.ts:75-110(getSrvCaptures)
프로덕션(origin/master) 의 createTargetModels 에는 TSLA-12556 에서 추가된 guard 가 존재한다:
async createTargetModels(cpSitetrack: CPSitetrack): Promise<void> {
logger.debug('SiteTrackPreprocessorRunner::createTargetModels | begin');
const cpBims = await this.createCPBims(cpSitetrack);
await this.validatesCPBims(cpSitetrack);
const cpCaptures = await this.createCPCaptures(cpSitetrack);
if (cpCaptures.length === 0) {
this.jobManager.setErrorCode(ErrorCode.Agent.DeviationCapturesNotFound);
throw new Error('No Captures found');
}
const uniqueLevelIds = this.getUniqueLevelIds(cpCaptures, cpSitetrack);
...
createCPCaptures 는 base runner 에서 getSrvCaptures 를 통해 서버에서 capture 목록을 받아온 뒤 필터 체인을 적용한다:
async getSrvCaptures(cpParent: CPSitetrack): Promise<TESLA.Capture[]> {
...
const srvCaptures = await this.cupixApi.sitetrack.getAllCaptures(cpParent.id);
...
let videoCaptures = srvCaptures
.filter(capture => capture.state === 'done')
.filter(capture => capture.error_code === null)
.filter(capture => capture.material === 'video')
.filter(capture => capture.expected_quality === TESLA.Capture.ExpectedQualityEnum.Dollhouse)
.filter(capture => capture.published_at); // <-- 이번 사건에서 탈락하는 조건
...
if (cpParent.levelId) {
videoCaptures = videoCaptures.filter(capture => capture.level?.id === cpParent.levelId);
}
...
return videoCaptures;
}
createCPCaptures 는 이 필터 결과로 CPCapture 를 만들어 cpParent.cpCaptures 를 반환하므로, 필터 결과가 empty 이면 반환값도 empty:
async createCPCaptures(cpParent: CPSitetrack): Promise<CPCapture[]> {
logger.debug('BasePreprocessorRunner::createCPCaptures | begin');
const cpCaptures = await createModels(CPCapture, cpParent, this.getSrvCaptures(cpParent));
...
return cpParent.cpCaptures;
}
PreprocessorService.run 은 이 throw 를 catch 하여 logger.error(..., error) 로 남기고 job 을 error 상태로 마킹한다:
} catch (error) {
logger.error('PreprocessorService::run | end', error);
await this.jobManager.updateErrorActionJob(this.ActionName);
}
기대 동작: sitetrack 대상 record 에 publish 된 video capture 가 최소 1개는 존재해야 preprocessor 가 정상 진행 가능. 실제 동작: record 137022 의 유일한 capture (731776) 가 published_at=null 이라 필터를 통과하지 못해 0건. Guard 가 정상적으로 fail-fast 했다.
Log Evidence#
Datadog 쿼리 (클러스터 파일과 동일):
service:cupixworks-sitetrack-preprocessor-agent status:error @environment:production "No Captures found"
Kibana (logstash-agents*) 쿼리 — sitetrack 20985 전체 흐름 추적:
index: logstash-agents*
query: sitetrack.id:20985
time: 2026-07-09T08:11:00Z .. 2026-07-09T08:12:30Z
핵심 디버그 로그 (KST 변환):
2026-07-09 17:11:50.428 DEBUG SiteTrackPreprocessorRunner::createTargetModels | begin
2026-07-09 17:11:50.561 DEBUG BasePreprocessorRunner::createCPBims | end - bim count: 5, bim ids: [15773, 15073, 15072, 14701, 14309]
2026-07-09 17:11:50.562 DEBUG BasePreprocessorRunner::createCPCaptures | begin
2026-07-09 17:11:50.562 DEBUG BasePreprocessorRunner::getSrvCaptures | begin
2026-07-09 17:11:50.613 DEBUG BasePreprocessorRunner::getSrvCaptures | load server Captures - count: 1, record id: 137022
2026-07-09 17:11:50.614 DEBUG BasePreprocessorRunner::getSrvCaptures | filer out video Captures - count: 0
2026-07-09 17:11:50.614 DEBUG BasePreprocessorRunner::getSrvCaptures | [Siteinsights] load server Captures - count: 0, record id: 137022
2026-07-09 17:11:50.614 DEBUG BasePreprocessorRunner::getSrvCaptures | capture count: 1, video capture count: 0, pano capture count(excluded): 1
2026-07-09 17:11:50.614 DEBUG BasePreprocessorRunner::getSrvCaptures | id: 731776, state: done, error_code: null, material: video, expected_quality: dollhouse, published_at: null
2026-07-09 17:11:50.615 DEBUG BasePreprocessorRunner::createCPCaptures | end
2026-07-09 17:11:50.616 ERROR No Captures found
2026-07-09 17:11:50.616 DEBUG JobManager::updateErrorActionJob | begin - jobId: 1183964, errorCode: AGT1802
핵심 데이터포인트: 서버는 capture 1건을 리턴했고, 그 capture 는 state=done, error_code=null, material=video, expected_quality=dollhouse 로 다른 조건은 전부 통과했지만 published_at: null 이어서 최종 필터(.filter(capture => capture.published_at))에서 탈락했다. 로그가 명시적으로 pano capture count(excluded): 1 이 아니라 video 필터 후 0건이라고 알려주므로 pano/quality 조건이 아니라 published_at 이 원인이라는 것이 명확하다.
Datadog attributes (원본 log entry):
{
"message": "No Captures found",
"status": "error",
"environment": "production",
"region": "us-west-2",
"sitetrack": { "id": 20985 },
"job": { "id": 1183964 },
"team": { "domain": "secc", "id": 285 },
"user": { "id": 50180, "email": "juhan121.lee@secc.co.kr" },
"tenant": "cupix",
"name": "Error",
"host": { "name": "ip-10-1-162-9.us-west-2.compute.internal" }
}
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Record 137022 의 유일 capture 가 published_at=null 이라 getSrvCaptures 의 .filter(c => c.published_at) 에서 탈락 → 0건 → TSLA-12556 guard 가 throw |
Kibana 로그가 id: 731776, ..., published_at: null 를 명시. video capture count: 0 직후 error. Guard 위치는 master sitetrack-preprocessor-runner.ts:16-19 |
— | Confirmed |
| H2 | levelId mismatch 로 필터 탈락 (capture.level?.id !== cpParent.levelId) |
levelId 필터는 코드 경로상 뒤에 위치 | 로그의 filer out video Captures - count: 0 이 levelId 필터 이전 단계에서 이미 0을 기록. 그리고 capture 는 애초에 published_at 필터에서 걸렸다 |
Rejected |
| H3 | expected_quality 나 material 이 맞지 않아 탈락 |
— | Kibana 로그가 material: video, expected_quality: dollhouse 로 두 조건 모두 통과했음을 확인 |
Rejected |
| H4 | 다른 관계 없는 시스템 오류 (network, DB) 로 인해 서버 capture 조회 자체가 실패 | — | load server Captures - count: 1, record id: 137022 로 서버 조회는 정상 (1건 반환). Filter 결과가 0인 것이지 조회 실패가 아님 |
Rejected |
| H5 | 전역 dependency (S3/RDS 등) 장애로 파생된 이슈 | — | status-board 조회 결과 svc:cupixworks-sitetrack-preprocessor-agent::unknown scope 에 active/recent 인시던트 없음. production burst 도 없이 단발성 발생 |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
- (운영) 없음. 이번 발생은 사용자가 publish 되지 않은 capture 를 대상으로 sitetrack job 을 트리거한 결과이므로 코드 상 데이터 무결성 위반은 없다. TSLA-12556 guard 가 의도대로 fail-fast 했다. Production 단발 1건이므로 hotfix 필요 없음.
단기 개선 (1주 이내)#
- 로그 레벨 재조정 검토:
applications/agents/packages/cupix-sitetrack-preprocessor-agent/src/preprocessor-service.ts:93-96의 catch 블록은 모든 예외를 일괄logger.error로 남기므로 사용자 precondition 위반도 시스템 오류처럼 monitoring 알림을 유발한다.error_code === AGT1802(그리고 유사한AGT180x계열 user-input errors —DeviationPointcloudsNotFound,DeviationTexturesNotFound,DeviationPhasesNotFound,DeviationElementsNotFound,DeviationTracksNotFound,DeviationAllElementsCompleted) 인 경우logger.warn으로 다운그레이드하는 것을 검토. 이때 blanket downgrade 는 지양 — 특정 error code 목록에 대해서만 warn 처리하고 나머지 unexpected exception 은 계속 error 유지. - 사용자 UX 개선: sitetrack job trigger API (tesla
cupixworks-api) 단계에서 대상 record 의 published video capture 존재 여부를 사전 검증하여, publish 되지 않은 경우 job 을 SQS 에 enqueue 하기 전에 400/409 로 거절하고 UI 에서 "capture publish 완료 후 다시 시도" 안내를 노출. 이렇게 하면 agent 레이어 error 발생 자체를 예방할 수 있다.
장기 개선 (재발 방지)#
- user error vs system error 분리 로깅 컨벤션: agents 전반에 걸쳐
ErrorCode.Agent.*중 user-input precondition 성격의 코드를 명시적으로 태깅 (예:USER_INPUTcategory) 하고,PreprocessorService.run의 catch 블록에서 태그별 로그 레벨을 결정. 다른 preprocessor agent (deviation, sitetrack-skipped) 도 동일 패턴을 공유하므로 base 레이어에서 처리하는 것이 자연스럽다. - capture publish 라이프사이클 이벤트 기반 트리거: sitetrack job 을 사용자가 수동 트리거하는 대신
capture.published이벤트를 소스로 workflow 를 시작하도록 재설계하면 preconditon 위반 자체가 물리적으로 발생하지 않음.
Monitoring#
모니터링 방향
AGT1802발생 빈도 추이 — 사용자 UX 개선의 필요성 판단 지표- Error / warn 레벨 분포 — user-input 원인 vs 실제 시스템 오류 구분
Datadog widget 쿼리 (release dashboard timeseries 용):
service:cupixworks-sitetrack-preprocessor-agent @environment:production status:error "No Captures found"
service:cupixworks-sitetrack-preprocessor-agent @environment:production "AGT1802"
service:cupixworks-sitetrack-preprocessor-agent @environment:production status:error
세 번째 쿼리는 preprocessor 전체 error 흐름을 확인하기 위한 baseline 이다. 위 세 쿼리 모두 writing-datadog-monitoring-queries 가이드에 따라 monitor-only 문법 (| stats, count by(...), threshold suffix) 을 피하고 순수 filter 만 사용한다.
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (즉시 코드 조치 없음), 단기 개선은 standard (로그 레벨 조정 + tesla 측 사전 검증 API 추가).