All elements are completed
RCA: All elements are completed
Overview#
What Happened#
2026-04-20 20:15 UTC에 cupixworks-sitetrack-preprocessor-agent 서비스에서 sitetrack 16662 (job 1025117)를 처리하는 도중 "All elements are completed" 에러가 발생했다. 이 에러는 SiteTrack의 모든 trackable element record가 이미 완료(completedAt 설정) 상태여서 preprocessor가 처리할 대상이 없음을 나타내는 expected operational scenario이지만, 코드에서 throw Error()로 처리되어 ERROR 레벨로 기록되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.message | All elements are completed |
| top_frame | SiteTrackPreprocessorRunner.validateTargetModels (app.cjs:8989) |
| env | production, us-west-2 |
| error_code | AGT1810 (DeviationAllElementsCompleted) |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| tpc (team 778) | 1 | Preprocessor job 1025117 실패 처리됨. SQS 메시지 삭제되어 재시도 없음. 사용자 ezimmerman@wdfinc.net에게 작업 실패로 표시됨. |
Timeline#
- 20:13:25 UTC — Job 1025117 수신, sitetrack 16662 로딩 시작
- 20:13:25 UTC —
load_target_models단계 진입 - 20:15:02 UTC —
validateTargetModels에서 모든 element가 completed 상태임을 감지, Error throw - 20:15:02 UTC — Job error 처리, workspace 정리 및 SQS 메시지 삭제
Error Log#
Error: All elements are completed
at SiteTrackPreprocessorRunner.validateTargetModels (/tmp/agent/dist/app.cjs:8989:13)
at /tmp/agent/dist/app.cjs:818:45
at Scope.activate (dd-trace/src/scope.js:25:14)
at TraceUtils.activateSpan (/tmp/agent/dist/app.cjs:792:43)
at descriptor.value (/tmp/agent/dist/app.cjs:817:47)
at PreprocessorService.run (/tmp/agent/dist/app.cjs:9644:26)
at async BaseService2.runByMessage (/tmp/agent/dist/app.cjs:6012:15)
at async BaseService2.runByMessages (/tmp/agent/dist/app.cjs:6000:13)
Impact#
- Service:
cupixworks-sitetrack-preprocessor-agent - Team: tpc
- 발생 횟수: 1
- 최초 발생: 2026-04-20T20:15:02.484Z
- 최근 발생: 2026-04-20T20:15:02.484Z
Root Cause Summary#
Sitetrack 16662의 모든 trackable element record가 이미 completed_at 값을 가지고 있어서 validateTargetModels()에서 "All elements are completed" 에러가 발생했다. 이 조건은 사용자가 이미 모든 작업을 완료한 SiteTrack에 대해 preprocessor job이 트리거된 정상적인 운영 시나리오이다. 그러나 코드에서 이를 throw Error()로 처리하고, catch 블록이 logger.error()로 기록하기 때문에 ERROR 레벨 로그가 생성된다. 실제로는 "처리할 대상 없음" 이라는 skip 조건이므로 error가 아닌 warn 또는 info 레벨이 적절하다.
Technical Analysis#
Code Path#
- Entry point:
PreprocessorService.run()— job 메시지를 수신하고 preprocessor 파이프라인을 실행한다.
@trace()
run = async (targetId: number, msgObject: any): Promise<void> => {
logger.info('PreprocessorService::run | begin');
try {
const srvJob = await this.jobManager.loadJob(targetId);
// ... job validation ...
if (srvJob) {
await this.jobManager.updateJobProgress(5, `load_sitetrack_on_${this.ActionName}`);
const cpSitetrack = await this.createCPSitetrack(srvJob);
if (cpSitetrack) {
// ...
await runner.createTargetModels(cpSitetrack);
await runner.validateTargetModels(cpSitetrack); // ← 여기서 throw
- Model loading:
SiteTrackPreprocessorRunner.createTargetModels()— BIM, capture, element, task, phase, element record 등 모든 모델을 API에서 로드한다.loadAllElementRecords()에서 서버의completed_at값을 각CPElementRecord에 설정한다.
async createTargetModels(cpSitetrack: CPSitetrack): Promise<void> {
// ... BIM, capture, pointcloud, element, task, texture, status, phase 로딩 ...
await this.loadAllElementRecords(cpSitetrack, cpElements, uniqueLevelIds);
}
- Element record population:
setFromSrvElementRecord()에서 서버의completed_at필드를_completedAt에 저장한다.
if (srvElementRecord instanceof TESLA.ElementRecord) {
this._completedAt = srvElementRecord.completed_at;
- Failure point:
BaseSiteinsightsPreprocessorRunner.validateTargetModels()— 모든 trackable element record의completedAt이 truthy인 경우 에러를 throw한다.
validateTargetModels(cpParent: CPSitetrack): void {
if (cpParent.trackableElementRecords.every(er => er.completedAt)) {
this.jobManager.setErrorCode(ErrorCode.Agent.DeviationAllElementsCompleted);
throw Error('All elements are completed');
}
}
- Error handling:
PreprocessorService.run()의 catch 블록에서logger.error()로 기록하고updateErrorActionJob()을 호출한다.
} catch (error) {
logger.error('PreprocessorService::run | end', error);
await this.jobManager.updateErrorActionJob(this.ActionName);
}
기대 동작 vs 실제 동작: 모든 element가 완료된 SiteTrack에 대해 preprocessor가 호출되면, 처리할 대상이 없으므로 graceful skip이 적절하다. 그러나 실제로는 Error를 throw하여 job이 error 상태로 전환되고, ERROR 레벨 로그가 생성된다.
Log Evidence#
Datadog에서 job 1025117의 전체 실행 흐름을 확인했다.
service:cupixworks-sitetrack-preprocessor-agent @sitetrack.id:16662
Job 시작부터 에러까지의 타임라인:
20:13:25.516 INFO BaseService::runByMessage | id: 1025117
20:13:25.557 INFO PreprocessorService::run | begin
20:13:25.557 INFO JobManager::loadJob | begin - job id: 1025117
20:13:25.601 INFO JobManager::loadJob | end - job id: 1025117
20:13:25.831 WARN JobManager::updateJobProgress | skipped - progress: 5, status: load_sitetrack_on_preprocessor
20:13:25.831 INFO PreprocessorService::createCPSitetrack | begin - sitetrackId: 16662
20:13:25.869 INFO PreprocessorService::createCPSitetrack | end
20:13:25.872 WARN JobManager::updateJobProgress | skipped - progress: 7, status: load_target_models
20:15:02.484 ERROR All elements are completed
20:15:02.754 INFO PreprocessorService::run | end
20:15:02.756 INFO AwsQueueManager::deleteMessage | begin
20:15:02.792 INFO AwsQueueManager::deleteMessage | end
주요 관찰 사항:
load_target_models단계에서 모델 로딩에 약 97초 소요 (20:13:25 → 20:15:02) — API 호출을 통해 element, task, phase, element record 등을 모두 로딩한 후 validation에서 실패JobManager::updateJobProgress | skipped로그가 2회 발생 — job progress 업데이트가 이미 해당 상태여서 skip됨- 에러 후 SQS 메시지가 정상 삭제됨 — 재시도 없이 job이 error로 종료됨
- 동시간대 다른 job (sitetrack 16657, job 1025116)은 정상 완료됨
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | 모든 element record가 이미 completed 상태인 SiteTrack에 대해 job이 트리거됨 (정상 시나리오, 잘못된 로그 레벨) | validateTargetModels() 코드가 .every(er => er.completedAt) 조건 확인 후 throw Error() 수행 (base-siteinsights-preprocessor-runner.ts:17-19). ErrorCode AGT1810이 "Deviation" 그룹(1800번대)에 속해 data validation 에러로 분류됨. catch 블록에서 logger.error()로 기록 (preprocessor-service.ts:95). |
— | Confirmed |
| H2 | Element record 로딩 중 데이터 누락으로 잘못된 completedAt 상태가 설정됨 | — | loadAllElementRecords()가 서버 API에서 element record를 정상 로딩하고, setFromSrvElementRecord()에서 completed_at 값을 직접 설정함 (cpelement-record.ts:68). 97초의 처리 시간은 대량 데이터 로딩의 정상 소요시간. |
Rejected |
| H3 | SiteTrack 16662에 trackable element가 0개여서 .every()가 빈 배열에 대해 true 반환 |
JavaScript의 .every()는 빈 배열에서 true 반환. |
createCPElements()에서 element가 0개면 DeviationElementsNotFound 에러가 먼저 발생 (sitetrack-preprocessor-runner.ts:147-149). createCPTasks()에서 trackable task가 0개면 DeviationTracksNotFound 에러가 먼저 발생 (sitetrack-preprocessor-runner.ts:182-184). 이 검증들이 validateTargetModels() 이전에 실행되므로, "All elements are completed"에 도달했다면 trackable element record가 1개 이상 존재했음. |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
BaseSiteinsightsPreprocessorRunner.validateTargetModels() (base-siteinsights-preprocessor-runner.ts:16-21)에서 throw Error()를 제거하고, 이 조건을 graceful skip으로 처리해야 한다.
PreprocessorService.run()(preprocessor-service.ts:72) 호출부에서validateTargetModels()의 반환값(boolean)을 확인하여 false일 경우 다음 단계를 skip하도록 변경- 또는
validateTargetModels()내부에서throw대신 warn 로그를 남기고 early return하는 방식으로 변경 - Error code
AGT1810설정은 유지하되, job 상태를 error가 아닌 완료(또는 skip)로 처리
단기 개선 (1주 이내)#
PreprocessorService.run()의 catch 블록(preprocessor-service.ts:94-97)에서 ErrorCode 기반으로 로그 레벨을 분기하는 로직 추가:AGT18xx계열 Deviation 에러는 warn 레벨로, 나머지는 error 레벨로 기록- 동일 패턴의 다른 validation 에러(
DeviationAllElementsOutOfRangeAGT1811,DeviationAllElementsCompletedWithinRangeAGT1812)도 동일하게 검토
장기 개선 (재발 방지)#
- Validation 결과를 exception이 아닌 result object 패턴으로 전환하여 expected skip 조건과 actual failure를 구조적으로 구분
- Job 트리거 시점에서 SiteTrack의 element 완료 상태를 사전 확인하여 불필요한 job 생성을 방지
Monitoring#
- 아래 Datadog 쿼리로 동일 에러 재발 모니터링:
service:cupixworks-sitetrack-preprocessor-agent status:error "All elements are completed"
- Fix 적용 후 해당 쿼리의 error 건수가 0으로 감소하고, warn 레벨로 전환되었는지 확인:
service:cupixworks-sitetrack-preprocessor-agent "All elements are completed" status:warn
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial —
validateTargetModels()의 throw를 warn 로그 + early return으로 변경하면 되며, 기존 테스트 케이스(base-siteinsights-preprocessor-runner.spec.ts:277-293)가 이미 이 시나리오를 커버하고 있어 수정 검증이 용이하다.