ES /docs

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#

  1. 2026-07-03 18:17:37 KSTPanoPostprocessorService download_panos 완료 (3.2초)
  2. 2026-07-03 18:17:53 ~ 18:20:07 KST — MaskWork::maskPanos, blur_panos, change_masks 등 pano 전처리 단계 정상 수행
  3. 2026-07-03 18:20:35 KSTcupix::pixgenie::mergeSegments() 완료 (484.72s)
  4. 2026-07-03 18:21:00 KSTPipeline result saved to: /tmp/workspace/capture_727017/output/pixgenie_result.json, DB version set to v1 (파이프라인 로직 성공)
  5. 2026-07-03 18:21:01 KSTdetectAndFixVideoFrameTimestampCorruption() start (뒷정리 단계 로그의 잔재)
  6. 2026-07-03 18:21:03 KSTSIGSEGV(11) in libfoundation.so::bootstrap::unload(); Node.js 부모 프로세스가 exit code 11 감지
  7. 2026-07-03 18:21:03 KSTPixGenieProcess::execute | failed - {} (error 객체가 빈 오브젝트로 직렬화됨), FileSystemManager::cleanupWorkspace 실행, capture state Error로 업데이트, PreprocessorService::run | end 로깅

Error Log#

Datadog Logs

text
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건뿐 (Datadog service: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#

  1. Entry point: preprocessor-service.ts:67PreprocessorService::run | begin
  2. preprocessor-service.ts:93pixGenieManager.executePixGenieProcessing(cpCapture) 호출
  3. pix-genie-manager.ts:75PixGenieProcess.execute 호출로 Python child spawn
  4. pixgenie.process.ts:65-70spawn('python3', [PIXGENIE_SCRIPT_PATH], ...)
  5. Python 프로세스가 정상 완료 후 exit 단계에서 native lib teardown 중 SIGSEGV → 커널이 exit code 11(=signal number) 반환
  6. Failure point: pixgenie.process.ts:105-112close 이벤트 handler가 code !== 0을 전부 실패로 처리
  7. preprocessor-service.ts:100-102 — catch 블록에서 updatePixGenieState(..., Error) + stringifyError(error) 로깅
applications/agents/packages/cupix-pix-genie-preprocessor-agent/src/process/pixgenie.process.ts:105-118typescript
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);
});
applications/agents/packages/cupix-pix-genie-preprocessor-agent/src/process/pixgenie.process.ts:32-40typescript
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;
}
applications/agents/packages/cupix-pix-genie-preprocessor-agent/src/preprocessor-service.ts:91-102typescript
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 쿼리:

text
service:cupixworks-pano-postprocessor-instance status:error @environment:production "PreprocessorService::run"
text
service:cupixworks-pano-postprocessor-instance "PixGenie" status:error   (retention 14d)

파이프라인 로직 성공 증거 (2026-07-03 18:21:00 KST, SIGSEGV 3초 전):

text
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에서 발췌):

text
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 대상):

text
"timestamp": "2026-07-03 18:21:03"
"message": "PixGenieProcess::execute | failed - {}"

pixgenie.process.ts:37JSON.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일 유사 발생 횟수:

text
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::unregisterObjectCreatorbootstrap::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
    typescript
    logger.error('PixGenieProcess::execute | failed - %s', JSON.stringify(error));
    
    는 Error 객체를 {}로 직렬화해 실제 스택/메시지가 유실된다. @agents/utils cplogger는 errorSafeFormat(applications/agents/packages/utils/src/cplogger.ts:6-37)에서 splat 위치의 Error를 {name, message, stack}으로 확장해 native 처리하므로, Error 객체를 그대로 넘기도록 변경한다.
    typescript
    logger.error('PixGenieProcess::execute | failed - %s', error);
    
    상위 catch(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#

text
service:cupixworks-pano-postprocessor-instance status:error "exit code 11"
text
service:cupixworks-pano-postprocessor-instance "SIGSEGV" "bootstrap::unload"
text
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:37logger.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-37errorSafeFormat 확인 — splat 위치의 Error를 {name, message, stack}으로 자동 확장. logger.error('%s', errorObject) 형태로 호출하면 별도 stringify 없이 stack 보존 가능함을 검증.