PreprocessorService::run | end - {"stack":"Error: PixGenie process failed with exit code 11. STDERR:
RCA: PixGenie process failed with exit code 11 (SIGSEGV in libfoundation unload)
Overview#
What Happened#
2026-07-03 18:21 KST, PixGenie 전처리 Python 프로세스가 capture_727017 작업을 정상 완료한 직후 프로세스 종료 시점에 libfoundation.so::bootstrap::unload() 경로에서 SIGSEGV(11)로 죽었다. 결과 파일(pixgenie_result.json)은 이미 디스크에 기록된 상태였지만, 프로세스가 exit code 11로 반환되면서 Node.js wrapper가 이를 실패로 판단해 capture 상태가 Error로 변경되었다. 지난 14일 동안 이 시그니처는 1건만 관측되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Error (Node.js) → SIGSEGV(11) in Python child |
| exception.message | PixGenie process failed with exit code 11 |
| top_frame | libfoundation.so : cupix::foundation::factory::detail::unregisterObjectCreator |
| failing binary | /tmp/lib/pixgenie/pixgenie/lib/pixgenieutil-x64-ubuntu22.04-gcc-release/libfoundation.so |
| child PID | 33 |
| capture_id | 727017 |
| env | production, us-west-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| cana / pano-postprocessor | 1 | 1개 capture(727017)의 PixGenie 전처리가 실패 상태로 마킹됨. 파이프라인 산출물(pixgenie_result.json)은 실제로 생성되었으나 exit code로 인해 활용되지 못함 |
Timeline#
- 2026-07-03 18:17:37 KST —
PanoPostprocessorServicedownload_panos완료 (3.2초) - 2026-07-03 18:17:53 ~ 18:20:07 KST — MaskWork::maskPanos, blur_panos, change_masks 등 pano 전처리 단계 정상 수행
- 2026-07-03 18:20:35 KST —
cupix::pixgenie::mergeSegments()완료 (484.72s) - 2026-07-03 18:21:00 KST —
Pipeline result saved to: /tmp/workspace/capture_727017/output/pixgenie_result.json,DB version set to v1(파이프라인 로직 성공) - 2026-07-03 18:21:01 KST —
detectAndFixVideoFrameTimestampCorruption()start (뒷정리 단계 로그의 잔재) - 2026-07-03 18:21:03 KST —
SIGSEGV(11)inlibfoundation.so::bootstrap::unload(); Node.js 부모 프로세스가 exit code 11 감지 - 2026-07-03 18:21:03 KST —
PixGenieProcess::execute | failed - {}(error 객체가 빈 오브젝트로 직렬화됨),FileSystemManager::cleanupWorkspace실행, capture stateError로 업데이트,PreprocessorService::run | end로깅
Error Log#
PreprocessorService::run | end - {"stack":"Error: PixGenie process failed with exit code 11. STDERR: ...
...
******* FATAL SIGNAL *******
file: , function: , Received fatal signal: SIGSEGV(11) PID: 33
***** SIGNAL SIGSEGV(11)
******* STACKDUMP *******
stack dump [1] /tmp/lib/pixgenie/pixgenie/lib/pixgenieutil-x64-ubuntu22.04-gcc-release/libg3log.so+0x15d5b [0x7f343ae7fd5b]
stack dump [2] /usr/lib/x86_64-linux-gnu/libc.so.6+0x42520 [0x7f35243f1520]
stack dump [3] /tmp/lib/pixgenie/pixgenie/lib/pixgenieutil-x64-ubuntu22.04-gcc-release/libfoundation.so : cupix::foundation::factory::detail::unregisterObjectCreator(std::type_info const&)+0xa1 [0x7f34145bfb51]
stack dump [4] /tmp/lib/pixgenie/pixgenie/lib/pixgenieutil-x64-ubuntu22.04-gcc-release/libfoundation.so : cupix::foundation::bootstrap::unload()+0x4d [0x7f34145eb07d]
stack dump [5] /lib64/ld-linux-x86-64.so.2+0x624e [0x7f352471b24e]
stack dump [6] /usr/lib/x86_64-linux-gnu/libc.so.6+0x45495 [0x7f35243f4495]
stack dump [7] /usr/lib/x86_64-linux-gnu/libc.so.6on_exit+0 [0x7f35243f4610]
stack dump [8] /usr/lib/x86_64-linux-gnu/libc.so.6+0x29d97 [0x7f35243d8d97]
stack dump [9] /usr/lib/x86_64-linux-gnu/libc.so.6__libc_start_main+0x80 [0x7f35243d8e40]
stack dump [10] /usr/bin/python3_start+0x25 [0x55a099cc0b45]
Impact#
- Service:
cupixworks-pano-postprocessor-instance - Team: cana
- 발생 횟수: 1
- 최초 발생: 2026-07-03 18:21 KST
- 최근 발생: 2026-07-03 18:21 KST
- Blast radius: capture 727017 1건. 14일 창(retention 전체)에서
SIGSEGV/exit code 11시그니처는 이 1건뿐 (Datadogservice:cupixworks-pano-postprocessor-instance "PixGenie" status:error검색 결과)
Root Cause Summary#
PixGenie 파이프라인은 정상적으로 완료되어 pixgenie_result.json을 저장하고 DB version set to v1까지 로깅했다. 그러나 Python 프로세스가 종료되면서 native 라이브러리(libfoundation.so)의 bootstrap::unload()가 atexit/on_exit 훅에서 호출될 때, factory::detail::unregisterObjectCreator(std::type_info const&)에서 SIGSEGV가 발생했다. 스택은 libc.so:on_exit → libfoundation.so:bootstrap::unload → factory::unregisterObjectCreator로, 이는 프로세스 exit 단계에서의 C++ 정적 소멸자 순서 문제(static destruction order fiasco) 혹은 libg3log/libfoundation이 관리하는 factory 레지스트리가 이미 해제된 상태에서 재접근하는 use-after-destroy 버그 시그니처와 일치한다. Node.js wrapper(PixGenieProcess)는 프로세스 exit code가 0이 아니면 무조건 실패로 처리하므로, 실제 산출물이 존재함에도 capture가 PixGenieState.Error로 마킹되었다.
Revision 1 fix scope: 본 revision에서는 logger 정정만 수행한다. Wrapper 판정 로직 강화, pixgenie native SIGSEGV 대응은 관측 가능성이 회복된 이후 별도 트랙으로 다룬다 — 현재 로그가 {}로 유실되어 재발 시 재조사 근거가 부족하기 때문이다.
Technical Analysis#
Code Path#
- Entry point:
preprocessor-service.ts:67—PreprocessorService::run | begin preprocessor-service.ts:93—pixGenieManager.executePixGenieProcessing(cpCapture)호출pix-genie-manager.ts:75—PixGenieProcess.execute호출로 Python child spawnpixgenie.process.ts:65-70—spawn('python3', [PIXGENIE_SCRIPT_PATH], ...)- Python 프로세스가 정상 완료 후 exit 단계에서 native lib teardown 중 SIGSEGV → 커널이 exit code
11(=signal number) 반환 - Failure point:
pixgenie.process.ts:105-112—close이벤트 handler가code !== 0을 전부 실패로 처리 preprocessor-service.ts:100-102— catch 블록에서updatePixGenieState(..., Error)+stringifyError(error)로깅
this._process.on('close', (code) => {
logger.debug('PixGenieProcess | process exited with code: %d', code);
if (code === 0) {
resolve();
} else {
reject(new Error(`PixGenie process failed with exit code ${code}${stderr ? `. STDERR: ${stderr}` : ''}`));
}
});
this._process.on('error', (error) => {
logger.error('PixGenieProcess | process error: %s', error.message);
reject(error);
});
try {
await this.runPythonProcess(params);
this.verifyOutput(params.outputDir);
logger.info('PixGenieProcess::execute | completed successfully');
} catch (error) {
logger.error('PixGenieProcess::execute | failed - %s', JSON.stringify(error));
throw error;
}
logger.info('PreprocessorService::run | Starting Pix Genie preprocessing');
const start = Date.now();
await this.pixGenieManager.executePixGenieProcessing(cpCapture);
logDuration(start, Date.now(), `executePixGenieProcessing - capture_id: ${cpCapture.id}`, 'PreprocessorService');
logger.info('PreprocessorService::run | Pix Genie preprocessing completed');
await this.pixGenieManager.updatePixGenieState(captureId, TESLA.PixGenieState.Done);
logger.info('PreprocessorService::run | successfully completed');
}
} catch (error) {
await this.pixGenieManager.updatePixGenieState(captureId!, TESLA.PixGenieState.Error);
logger.error('PreprocessorService::run | end - %s', stringifyError(error));
기대 동작: Python 프로세스가 파이프라인을 완료하고 exit 0으로 정상 종료. wrapper는 pixgenie.sqlite 산출물을 검증하고 후속 업로드 진행.
실제 동작: 파이프라인 로직은 성공하여 pixgenie_result.json을 저장했으나, Python interpreter shutdown 시 libfoundation.so::bootstrap::unload()에서 native factory 레지스트리 해제 도중 SIGSEGV. 커널이 exit code 11을 반환하고 wrapper는 이를 완전 실패로 처리해 capture 상태를 Error로 마킹.
Log Evidence#
Datadog 쿼리:
service:cupixworks-pano-postprocessor-instance status:error @environment:production "PreprocessorService::run"
service:cupixworks-pano-postprocessor-instance "PixGenie" status:error (retention 14d)
파이프라인 로직 성공 증거 (2026-07-03 18:21:00 KST, SIGSEGV 3초 전):
PixGenieProcess | [2026-07-03T09:21:00.265Z] Pipeline result saved to: /tmp/workspace/capture_727017/output/pixgenie_result.json
PixGenieProcess | [2026-07-03T09:21:00.355Z] DB version set to v1
PixGenieProcess | No upload URL provided. Output file will not be uploaded.
PixGenieProcess | To upload, set the CPX_UPLOAD_URL environment variable.
Segfault 발생 시점 스택 (representative error에서 발췌):
file: , function: , Received fatal signal: SIGSEGV(11) PID: 33
***** SIGNAL SIGSEGV(11)
stack dump [3] libfoundation.so : cupix::foundation::factory::detail::unregisterObjectCreator(std::type_info const&)+0xa1
stack dump [4] libfoundation.so : cupix::foundation::bootstrap::unload()+0x4d
stack dump [5] /lib64/ld-linux-x86-64.so.2+0x624e
stack dump [7] libc.so.6 on_exit+0
stack dump [9] libc.so.6 __libc_start_main+0x80
에러 객체 직렬화 문제 (본 revision의 fix 대상):
"timestamp": "2026-07-03 18:21:03"
"message": "PixGenieProcess::execute | failed - {}"
pixgenie.process.ts:37의 JSON.stringify(error)는 Error 객체의 enumerable 프로퍼티가 비어있어 항상 {}로 직렬화된다. 상위 catch(preprocessor-service.ts:138, stringifyError 사용)에서만 stack이 남아 조사가 가능했다. @agents/utils cplogger는 이미 Error를 native로 처리하는 errorSafeFormat을 갖고 있어(cplogger.ts:6-37, splat 위치의 Error를 {name, message, stack}으로 확장), Error를 직접 splat 인자로 전달하기만 하면 이 문제가 해소된다.
지난 14일 유사 발생 횟수:
Datadog query: service:cupixworks-pano-postprocessor-instance "PixGenie" status:error (now-14d)
→ 1 match (2026-07-03 18:21:03 KST, this cluster only)
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Python 프로세스가 처리 중간에 크래시 (파이프라인 실패) | exit code 11, SIGSEGV | 크래시 3초 전에 Pipeline result saved to: .../pixgenie_result.json + DB version set to v1 로그가 남음 — 파이프라인 완료 상태 |
Rejected |
| H2 | OOM Killer에 의한 프로세스 종료 | 대용량 이미지/ML 처리 | 로그가 SIGKILL이 아닌 SIGSEGV(11)이고, 자체 스택덤프가 남았음 (OOM 시 커널이 즉시 SIGKILL, 사용자 handler 실행 불가) | Rejected |
| H3 | native 라이브러리 teardown 시점 use-after-destroy / static destruction order fiasco (libfoundation.so::bootstrap::unload) |
stack dump [3]~[4]가 factory::unregisterObjectCreator → bootstrap::unload, stack [7]이 on_exit — 프로세스 exit 훅에서 발생. 파이프라인 결과는 이미 저장됨 |
— | Confirmed |
| H4 | 입력 데이터(capture 727017) 특성으로 인한 결정론적 크래시 | 특정 capture에서만 발생 | 14일 창 전체에서 오직 1건, 재현 시도 자체가 아직 없음 — 데이터 특이성보다는 종료 경로 타이밍 이슈에 가까움 (uncertain — needs verification) | Inconclusive |
| H5 | Node.js wrapper의 프로세스 timeout으로 인한 강제 abort (TIMEOUT_MS = 2h) |
AbortController 사용 | Pipeline result saved to 로그가 정상적으로 남아 있고 wrapper warn('process timeout') 로그가 없음 |
Rejected |
Fix Recommendation#
Revision 1 사용자 피드백에 따라 fix scope를 logger 수정 한 건으로 좁힌다. Wrapper의 exit-code 판정 로직, pixgenie native 크래시 대응(libfoundation SIGSEGV)은 별도 트랙으로 분리한다 — 이 revision에서는 관측 가능성 회복만 목표로 한다.
즉시 조치 (Logger only)#
- 에러 로깅 정정:
applications/agents/packages/cupix-pix-genie-preprocessor-agent/src/process/pixgenie.process.ts:37의는 Error 객체를typescriptlogger.error('PixGenieProcess::execute | failed - %s', JSON.stringify(error));{}로 직렬화해 실제 스택/메시지가 유실된다.@agents/utilscplogger는errorSafeFormat(applications/agents/packages/utils/src/cplogger.ts:6-37)에서 splat 위치의 Error를{name, message, stack}으로 확장해 native 처리하므로, Error 객체를 그대로 넘기도록 변경한다.상위 catch(typescriptlogger.error('PixGenieProcess::execute | failed - %s', error);preprocessor-service.ts:138,stringifyError사용)와 동일한 수준의 stack이 남아 이후 SIGSEGV 재조사 시 활용 가능해진다.
범위 밖 (본 fix 에서 다루지 않음)#
- Wrapper의 exit code 판정 로직 (
pixgenie.process.ts:105-113). - Native SIGSEGV 자체 (
libfoundation.so::bootstrap::unload) — pixgenie 팀 소관. - capture 727017 상태 정정 — 파이프라인 산출물은 저장 완료이므로 별도 운영 판단 필요.
로그 개선 배포 후 동일 시그니처(SIGSEGV in libfoundation.so::bootstrap::unload) 재발 여부를 관측하며, 재발 시 로그가 개선된 상태에서 재조사한다.
Monitoring#
service:cupixworks-pano-postprocessor-instance status:error "exit code 11"
service:cupixworks-pano-postprocessor-instance "SIGSEGV" "bootstrap::unload"
service:cupixworks-pano-postprocessor-instance "Pipeline result saved to" "SIGSEGV"
세 쿼리 모두 writing-datadog-monitoring-queries 가이드에 따라 | stats, count by(...) 등의 monitor-only 문법 없이 raw log 검색으로 작성. release dashboard timeseries widget에 그대로 사용 가능.
Risk Assessment#
- Risk level: low (14일 창에서 1건, 파이프라인 결과 자체는 저장 성공)
- 예상 복잡도: trivial (Revision 1 scope 기준:
pixgenie.process.ts:37한 줄의 splat 인자 변경. Wrapper 판정/native SIGSEGV는 이번 fix scope 밖)
Revision History#
Revision 1#
Feedback: "logger 만 수정하는 방향으로 보고서 수정" — fix scope를 logger 정정으로 한정.
판정:
| 피드백 항목 | 판정 | 근거 |
|---|---|---|
| Fix scope를 logger 수정으로 한정 | 수용 | pixgenie.process.ts:37의 logger.error('... - %s', JSON.stringify(error))는 실제 로그에서 PixGenieProcess::execute | failed - {} 로 관측 — Error 객체가 enumerable 프로퍼티가 없어 JSON.stringify가 {}로 직렬화. @agents/utils cplogger의 errorSafeFormat(cplogger.ts:6-37)이 splat 위치의 Error를 이미 native로 처리하므로 splat 인자만 Error 객체로 변경하면 stack/message가 복원된다. 관측성이 우선 복구되어야 이후 SIGSEGV 재발 시 재조사가 가능하므로, 이번 revision을 logger fix에 한정하는 방향은 타당. |
| Wrapper exit-code 판정 로직 강화 제외 | 수용 | 원 보고서의 "즉시 조치" 첫 항목이었으나 사용자 지시에 따라 Fix Recommendation의 "범위 밖" 항목으로 이동. pixgenie.process.ts:105-113의 close handler는 그대로 유지되며, 향후 별도 트랙으로 다룰 여지를 남김. |
| Pixgenie 팀 대상 native 대응 제외 | 수용 | libfoundation.so::bootstrap::unload SIGSEGV는 pixgenie 바이너리 소관이며 error-sweeper 코드베이스 변경 대상이 아니다. Fix Recommendation의 "범위 밖" 항목으로 이동. |
원 보고서 JSON.stringify(error, Object.getOwnPropertyNames(error)) 인용 오류 |
수용 | 실제 코드는 pixgenie.process.ts:37에서 JSON.stringify(error)(단일 인자). 원 보고서가 잘못 인용한 것을 확인하여 Log Evidence와 Code Path 스니펫을 실제 소스에 맞게 정정. |
변경 사항:
Root Cause Summary에 revision 1 scope 문단 추가 (logger 정정에 한정한다는 명시).Technical Analysis > Code Path의 스니펫을 실제 소스(JSON.stringify(error))에 맞게 정정.Log Evidence의 부수 관찰 문단을 fix 대상 문단으로 재작성. cplogger의errorSafeFormat동작(cplogger.ts:6-37)을 근거로 splat 전달 방식이 문제를 해소함을 명시.Fix Recommendation을 전면 재구성: 즉시 조치는 logger 한 항목, 나머지는 "범위 밖"으로 이관.Risk Assessment복잡도를 revision 1 scope에 맞춰 trivial로 조정.
추가 조사 내용:
applications/agents/packages/cupix-pix-genie-preprocessor-agent/src/process/pixgenie.process.ts:37소스 재확인 — 원 보고서 인용 오류 발견.applications/agents/packages/utils/src/cplogger.ts:6-37의errorSafeFormat확인 — splat 위치의 Error를{name, message, stack}으로 자동 확장.logger.error('%s', errorObject)형태로 호출하면 별도 stringify 없이 stack 보존 가능함을 검증.