ES /docs

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=731776published_atnull 이어서, 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#

  1. 2026-07-09 17:11:50.156 KST — Preprocessor agent 가 SQS 메시지 수신, job 1183964 로딩 시작 (session 295fac4cbfecce9fd33646184589507a6946aadf).
  2. 2026-07-09 17:11:50.398 KSTPreprocessorService::createCPSitetrack | begin - sitetrackId: 20985.
  3. 2026-07-09 17:11:50.561 KSTBasePreprocessorRunner::createCPBims | end - bim count: 5 (BIM 은 정상 로드).
  4. 2026-07-09 17:11:50.613 KSTgetSrvCaptures | load server Captures - count: 1, record id: 137022 (record 에 capture 1개 존재).
  5. 2026-07-09 17:11:50.614 KSTgetSrvCaptures | id: 731776, state: done, error_code: null, material: video, expected_quality: dollhouse, published_at: nullpublished_at: null 로 인해 필터 탈락.
  6. 2026-07-09 17:11:50.614 KSTgetSrvCaptures | filer out video Captures - count: 0createCPCaptures | end.
  7. 2026-07-09 17:11:50.616 KSTError: No Captures found throw, JobManager::updateErrorActionJob | begin - errorCode: AGT1802.
  8. 2026-07-09 17:11:50.765 KST — SQS 메시지 삭제, job 종료.

Error Log#

Datadog Logs

text
No Captures found

스택 트레이스 원본:

Datadog @attributes.stacktext
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 를 가지고 있다. createCPCapturesBaseSiteinsightsPreprocessorRunner.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 (production master)
  • 필터 로직: applications/agents/packages/siteinsights/src/runner/base-siteinsights-preprocessor-runner.ts:75-110 (getSrvCaptures)

프로덕션(origin/master) 의 createTargetModels 에는 TSLA-12556 에서 추가된 guard 가 존재한다:

applications/agents/packages/cupix-sitetrack-preprocessor-agent/src/runner/sitetrack-preprocessor-runner.ts:9-20 (master)typescript
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 목록을 받아온 뒤 필터 체인을 적용한다:

applications/agents/packages/siteinsights/src/runner/base-siteinsights-preprocessor-runner.ts:75-110typescript
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:

applications/agents/packages/siteinsights/src/runner/base-siteinsights-preprocessor-runner.ts:41-57typescript
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 상태로 마킹한다:

applications/agents/packages/cupix-sitetrack-preprocessor-agent/src/preprocessor-service.ts:93-96typescript
} 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 쿼리 (클러스터 파일과 동일):

text
service:cupixworks-sitetrack-preprocessor-agent status:error @environment:production "No Captures found"

Kibana (logstash-agents*) 쿼리 — sitetrack 20985 전체 흐름 추적:

text
index: logstash-agents*
query: sitetrack.id:20985
time:  2026-07-09T08:11:00Z .. 2026-07-09T08:12:30Z

핵심 디버그 로그 (KST 변환):

Kibana logstash-agents* — sitetrack 20985 실행 흐름text
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):

Datadog log attribute snapshotjson
{
  "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_qualitymaterial 이 맞지 않아 탈락 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_INPUT category) 하고, PreprocessorService.run 의 catch 블록에서 태그별 로그 레벨을 결정. 다른 preprocessor agent (deviation, sitetrack-skipped) 도 동일 패턴을 공유하므로 base 레이어에서 처리하는 것이 자연스럽다.
  • capture publish 라이프사이클 이벤트 기반 트리거: sitetrack job 을 사용자가 수동 트리거하는 대신 capture.published 이벤트를 소스로 workflow 를 시작하도록 재설계하면 preconditon 위반 자체가 물리적으로 발생하지 않음.

Monitoring#

모니터링 방향

  1. AGT1802 발생 빈도 추이 — 사용자 UX 개선의 필요성 판단 지표
  2. Error / warn 레벨 분포 — user-input 원인 vs 실제 시스템 오류 구분

Datadog widget 쿼리 (release dashboard timeseries 용):

text
service:cupixworks-sitetrack-preprocessor-agent @environment:production status:error "No Captures found"
text
service:cupixworks-sitetrack-preprocessor-agent @environment:production "AGT1802"
text
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 추가).