Process terminated with non-zero code: 1
RCA: Process terminated with non-zero code: 1
Overview#
What Happened#
2026-04-20 23:13:59 UTC에 cupixworks-capture-3dreconstruction-instance 서비스의 EU 리전(eu-central-1) 인스턴스에서 capture 33815에 대한 densemapper 외부 프로세스가 약 67분간 실행 후 exit code 1로 종료되었다. 에러 객체가 빈 {}으로 직렬화되어 실패 원인에 대한 구체적 정보가 손실되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Error |
| exception.message | Process terminated with non-zero code: 1 |
| top_frame | ChildProcessManager.handleMessage (child-process.manager.ts:182) |
| env | production, eu-central-1 |
Timeline#
- 2026-04-20T22:06:31Z — Job 90769 시작, capture 33815 인증 및 리소스 로딩
- 2026-04-20T22:06:48Z — densemapper 프로세스 실행 시작 (domain: innovo, region: us-west-2)
- 2026-04-20T23:13:59.288Z — densemapper 프로세스 exit code 1로 종료
- 2026-04-20T23:13:59.289Z — 에러 로깅 (빈 객체
{}) - 2026-04-20T23:13:59.696Z — 서비스 force shutdown 시작
Error Log#
Process terminated with non-zero code: 1
Impact#
- Service:
cupixworks-capture-3dreconstruction-instance - Team: innovo
- 발생 횟수: 1
- 최초 발생: 2026-04-20T23:13:59.288Z
- 최근 발생: 2026-04-20T23:13:59.288Z
Root Cause Summary#
densemapper 외부 바이너리 프로세스가 내부 오류(메모리 부족, 입력 데이터 문제, 또는 알고리즘 실패)로 exit code 1을 반환했다. 에이전트 코드에서 JSON.stringify(error)로 Error 객체를 직렬화할 때 Error 인스턴스의 message와 stack 속성이 포함되지 않아 빈 {}가 기록되었다. 이로 인해 densemapper가 실패한 구체적 원인(stdout/stderr에 출력된 정보)이 상위 프로세스의 로그에 전파되지 않았다. densemapper는 별도의 child process(ThreeDReconstructionProcess)에서 child_process.spawn으로 실행되며, 그 child process의 stdout/stderr는 this.log()로 기록되지만 IPC 메시지를 통해 부모에게 에러 내용이 전달될 때 Error 객체로 감싸지면서 원본 메시지가 유실된다.
Technical Analysis#
Code Path#
- Entry point:
three-d-reconstruction-service.ts:57—ThreeDReconstruction.init()호출 - Reconstruction 실행:
three-d-reconstruction-service.ts:306—threeDReconstructorManager.execute(params)호출 - Manager 위임:
three-d-reconstructor.manager.ts:37—childProcessManager.execute('execute', params)호출 - IPC 통신:
child-process.manager.ts:186— 메시지를 child process로 전송하고 응답 대기 - Child process에서 spawn:
three-d-reconstruction.process.ts:57—child_process.spawn(command, [], { shell: true })실행 - Exit 감지:
three-d-reconstruction.process.ts:64-67— exit code !== 0일 때 Error 생성 및 reject - 에러 전파:
child-process.manager.ts:182— child process의 응답에서pending.reject(new Error(response.error))실행 - Failure point:
three-d-reconstruction-service.ts:322— catch 블록에서JSON.stringify(error)호출 시 빈{}출력
_spawn.on('exit', (code, signal) => {
if (code === null) {
const errorMessage = `Process terminated unexpectedly by signal: ${signal}`;
this.log(`ThreeDReconstructionProcess::execute | ${errorMessage}`);
reject(new Error(errorMessage));
} else if (code !== 0) {
const errorMessage = `Process terminated with non-zero code: ${code}`;
this.log(`ThreeDReconstructionProcess::execute | ${errorMessage}`);
reject(new Error(errorMessage));
}
exit code 1 감지 시 new Error(errorMessage)로 reject하며, 이 Error가 IPC를 통해 부모 프로세스로 전달된다.
} catch (error: any) {
logger.error(`ThreeDReconstruction::runThreeDReconstruction | Capture ID: ${cpCapture.id} | Error: %s`, JSON.stringify(error));
this.jobManager.setErrorCode(ErrorCode.Densemapper.Execute);
throw new Error(`Failed to execute densemapper for capture ID: ${cpCapture.id} - error: ${JSON.stringify(error)}`);
}
JSON.stringify(error)는 Error 객체에 대해 {}를 반환한다. Error.message와 Error.stack은 non-enumerable 속성이므로 JSON 직렬화에 포함되지 않는다. 이것이 로그에 Error: {}로 기록되는 원인이다.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-capture-3dreconstruction-instance status:error @environment:production
service:cupixworks-capture-3dreconstruction-instance @capture.id:33815
핵심 로그 시퀀스 (capture 33815):
2026-04-20T22:06:31.276Z INFO ThreeDReconstruction::init
2026-04-20T22:06:31.333Z INFO ThreeDReconstruction::authenticate | end
2026-04-20T22:06:31.334Z INFO JobManager::loadJob | begin - job id: 90769
2026-04-20T22:06:31.783Z INFO ThreeDReconstruction::loadVideos | video count: 1
2026-04-20T22:06:31.841Z INFO ThreeDReconstruction::loadClusters | cluster count: 1
2026-04-20T22:06:48.167Z INFO ThreeDReconstruction::runThreeDReconstruction environments | domain: innovo, envName: production, launchMode: CUPIXWORKS, region: us-west-2, userEmail: undefined
67분간 중간 로그 없음 (densemapper 바이너리 실행 중)
2026-04-20T23:13:59.288Z ERROR Process terminated with non-zero code: 1
at ChildProcessManager3.handleMessage (/tmp/agent/dist/app.cjs:5219:26)
at ChildProcess.<anonymous> (/tmp/agent/dist/app.cjs:5157:16)
2026-04-20T23:13:59.289Z ERROR ThreeDReconstruction::runThreeDReconstruction | Capture ID: 33815 | Error: {}
2026-04-20T23:13:59.290Z ERROR Failed to execute densemapper for capture ID: 33815 - error: {}
at ThreeDReconstruction.runThreeDReconstruction (/tmp/agent/dist/app.cjs:8318:15)
at ThreeDReconstruction.run (/tmp/agent/dist/app.cjs:8142:11)
at ThreeDReconstruction.init (/tmp/agent/dist/app.cjs:8093:7)
2026-04-20T23:13:59.696Z INFO ThreeDReconstruction::terminateService | force shutdown after 10 seconds
동일 시간대 별도 호스트에서 capture 33817에 대한 관련 에러:
2026-04-20T22:55:49.695Z ERROR not found cpc files
at ThreeDReconstruction.checkPointcloudFiles (/tmp/agent/dist/app.cjs:8329:15)
host: 9990219feaf0
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | densemapper 바이너리 내부 실행 실패 (OOM, 입력 데이터 에러, 알고리즘 에러) | 67분간 정상 실행 후 exit code 1 반환; 에이전트 코드 자체에는 종료를 트리거하는 로직 없음; densemapper는 C++ 네이티브 바이너리로 내부 상태를 Node.js에 전달하지 않음 | densemapper 내부 로그 확인 불가 (stdout/stderr가 child process 내부에서만 this.log()로 기록되며 부모에게 구조화된 에러 메시지로 전달되지 않음) |
Confirmed |
| H2 | ChildProcessManager 타임아웃으로 프로세스 강제 종료 | executeTimeoutMs 옵션이 존재하며 SIGKILL 전송 코드가 있음 (child-process.manager.ts:221) |
타임아웃 시 에러 메시지는 Execution timeout after Xms이며, 실제 에러는 Process terminated with non-zero code: 1; signal이 아닌 exit code로 종료됨 |
Rejected |
| H3 | 입력 비디오 파일 다운로드 실패 | 같은 시간대 capture 33817에서 not found cpc files 에러 발생 |
capture 33815는 video count: 1로 정상 로딩 확인됨; densemapper가 67분간 실행된 것은 입력 파일이 존재했음을 의미 | Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
three-d-reconstruction-service.ts:322—JSON.stringify(error)를error instanceof Error ? error.message : JSON.stringify(error)로 변경하여 Error 객체의 메시지를 정상 기록- 이 패턴은 같은 파일의
throw new Error(...)구문(line 324)에서도 동일하게 적용 필요
단기 개선 (1주 이내)#
three-d-reconstruction.process.ts:74-75— densemapper의 stdout/stderr 출력을 단순this.log()가 아닌 구조화된 버퍼로 수집하여, exit code 1 발생 시 마지막 N줄을 에러 메시지에 포함시킬 것. 이렇게 하면 densemapper 내부 오류 원인을 부모 프로세스 로그에서 확인 가능- child process에서 부모로 에러를 전달할 때
error.message를 포함하도록 BaseProcess의 응답 직렬화 로직 개선
장기 개선 (재발 방지)#
- densemapper 바이너리가 실패 시 구조화된 에러 코드/메시지를 stdout이나 특정 파일에 남기도록 표준화
- 에이전트 전체에서
JSON.stringify(error)패턴을 검수하여 Error 객체 직렬화 문제를 일괄 수정 - densemapper 실행 중 주기적 health check (메모리 사용량, 진행률) 로깅 추가
Monitoring#
- densemapper exit code별 발생 빈도 추적:
service:cupixworks-capture-3dreconstruction-instance "Process terminated with non-zero code"
- densemapper 실행 시간 분포 모니터링 (67분 이상 실행 후 실패하는 패턴 감지):
service:cupixworks-capture-3dreconstruction-instance "runThreeDReconstruction" "begin" OR "end"
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard
- 단일 capture(33815)에 영향, innovo 팀의 bot 계정에서 실행된 작업. densemapper 바이너리 내부 실패는 입력 데이터에 의존적이며 시스템적 장애가 아닌 개별 작업 실패로 판단된다. 다만 에러 정보 손실(
JSON.stringify이슈)은 모든 densemapper 실패에 영향을 미치므로 관찰 가능성(observability) 측면에서 수정이 필요하다.