Process killed by signal SIGABRT (code: null)
RCA: Process killed by signal SIGABRT
Overview#
What Happened#
2026-07-15 06:09:09 KST, production us-west-2 리전의 cupixworks-capture-refinement-arm-instance 서비스에서 scenemapper.process 자식 프로세스가 SIGABRT 시그널로 비정상 종료되었다. 부모 Node.js 프로세스는 ChildProcessManager의 close 이벤트 핸들러에서 이 시그널을 관측하고 Error: Process killed by signal SIGABRT (code: null)을 error 레벨로 기록했으며, 이후 ScenemapperManager::execute가 실패하며 refinement job 1196597이 실패 처리되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Error (Node.js) |
| exception.message | Process killed by signal SIGABRT (code: null) |
| top_frame | /tmp/agent/dist/app.cjs:5248:33 (bundled child-process.manager.ts close handler) |
| runtime | Node.js child_process fork, native addon scenemapper_api.node |
| deploy | bundled at /tmp/agent/dist/app.cjs (offset shifted from :5307 → :5248 between 2026-07-11 and 2026-07-15, redeploy 존재) |
| env | production, region us-west-2, tenant cupix, arm instance |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| lemoine / capture refinement | 1 (이번 클러스터) + 최근 14일 20건 이상의 유사 SIGABRT | Refinement job 실패로 해당 capture의 refined alignment 산출물 미생성, 후속 3D reconstruction 파이프라인 차단 |
Timeline#
- 2026-07-15 06:08:20 KST —
RefinementService::init/authenticate/ job1196597load - 2026-07-15 06:08:24 KST — 대량의
[123475.07] | video filepath not found - 694025.insvwarn 로그 (native scenemapper 라이브러리 출력) - 2026-07-15 06:08:53 KST —
[122204.00] | Failed unstitch refinement - landmark visible count decreased. Revert to the stitched state.warn 로그 - 2026-07-15 06:09:09 KST — 자식 프로세스 SIGABRT 종료;
ChildProcessManager::setupEventHandlers | Child process closed,Child process exited,Process killed by signal SIGABRT (code: null)error 로그 - 2026-07-15 06:09:09 KST —
ScenemapperManager::execute | error - ...warn 로그와 함께RefinerExecute(AGT3012) 에러코드 세팅 및 job 실패 처리 - 2026-07-15 06:09:10 KST —
RefinementService::terminateService | force shutdown after 10 seconds - 2026-07-15 06:11:34 KST — 새 인스턴스가 다음 job
1196611처리 개시 (서비스 자체는 회복)
Error Log#
Process killed by signal SIGABRT (code: null)
Impact#
- Service:
cupixworks-capture-refinement-arm-instance - Team: lemoine
- 발생 횟수: 1 (본 클러스터 fingerprint 기준). 단, 동일 원인(SIGABRT in scenemapper child)의 warn 이벤트는 14일 창에서 20건 이상 관측됨 — 만성적 recurrence 이슈.
- 최초 발생: 2026-07-15 06:09:09 KST
- 최근 발생: 2026-07-15 06:09:09 KST
Root Cause Summary#
Refinement agent는 native C++ addon (scenemapper_api.node)을 forked child process(process/scenemapper.process.ts) 안에서 로드해 Refiner.process()를 호출한다. 네이티브 코드 내부에서 abort()가 트리거되어 (전형적으로 std::terminate, C++ assertion, uncaught native exception, 또는 메모리 이상) 자식 프로세스가 SIGABRT로 종료되었다. 부모의 ChildProcessManager.setupEventHandlers는 이를 close 이벤트에서 감지해 pending promise를 Error: Process killed by signal SIGABRT (code: null) 로 reject하고 error 로그를 남긴다. 즉 관측된 에러는 네이티브 라이브러리의 crash가 TypeScript 레이어까지 전파된 표면적 증상이며, 진짜 원인은 native scenemapper 라이브러리의 특정 입력(landmark visible count 감소 → unstitch revert 시나리오)에 대한 미처리 실패 경로에 있다.
Technical Analysis#
Code Path#
Entry point: refinement-service.ts:79 (RefinementService::run) → runRefinement (line 305) → ScenemapperManager::execute → ChildProcessManager::execute → forked child scenemapper.process.ts → Refiner.process() (native).
- 부모: refinement 실행 호출 —
Refiner.process()를 IPC로 자식에게 위임한다.
execute = async (params: RefinerParams): Promise<void> => {
await this.ensureInitialized();
try {
await this.childProcessManager.execute('execute', params);
} catch (error) {
logger.warn('ScenemapperManager::execute | error - %s', error);
if (this.setJobErrorCode) this.setJobErrorCode(ErrorCode.ScenemapperUtils.RefinerExecute);
throw error;
}
};
- 자식: 네이티브 라이브러리 실행 지점 — 이 라인 내부(C++)에서
abort()가 발생하여 SIGABRT.
private execute = (params: ExecuteMessage): void => {
this.log(`ScenemapperProcess::execute | params: ${JSON.stringify(params)}`);
if (!this.libPath || !params?.refinerParamsFilepath) {
throw new Error('ScenemapperProcess::execute | Required parameters missing');
}
// eslint-disable-next-line @typescript-eslint/no-require-imports
const ScenemapperApi = require(this.libPath);
const Refiner = new ScenemapperApi.refiner();
Refiner.load_params(params.refinerParamsFilepath);
Refiner.process(); // ← 네이티브 크래시 지점 (SIGABRT)
};
- Failure point: 부모의 close 이벤트 핸들러 — 스택 트레이스가 가리키는
app.cjs:5248:33에 해당하는 소스는 아래 handler.signal이 truthy이므로"Process killed by signal SIGABRT (code: null)"문자열이 그대로 생성되어 pending promise reject 및 로그 출력.
this.process.on('close', (code: number | null, signal: string | null) => {
logger.error('ChildProcessManager::setupEventHandlers | Child process closed', {
code,
signal
});
const errorMsg = signal
? `Process killed by signal ${signal} (code: ${code})`
: `Process exited with code ${code}`;
this.rejectAllPending(new Error(errorMsg));
this.process = undefined;
this.emit('close', code, signal);
});
- 부모: run 루프의 catch 및 job 실패 처리
private run = async (): Promise<void> => {
logger.info('RefinementService::run | begin');
try {
const srvJob = await this.jobManager.loadJob(Environment.CPX_JOB_ID as number);
// ...
await this.runRefinement(cpCapture);
// ...
await this.jobManager.updateCompleteActionJob('refinement', TESLA.UpdateJobRequest.StateEnum.Stopped);
} catch (error) {
logger.error('RefinementService::run | end - %s', JSON.stringify(error));
await this.jobManager.updateErrorActionJob('refinement');
}
};
기대 동작: Refiner.process()가 성공/실패를 IPC 응답으로 반환하고, 실패 시에도 자식 프로세스가 정상 종료되어 부모가 도메인 에러로 처리한다.
실제 동작: native C++ 레이어에서 abort()가 발생하여 자식이 SIGABRT로 즉시 종료되고, ScenemapperApi가 로그에 남긴 "landmark visible count decreased" / "video filepath not found" 이후의 특정 분기에서 assertion/undefined behavior가 발생한 것으로 추정. 자식 프로세스에는 uncaughtException 훅이 있어도 native abort는 잡을 수 없다.
Log Evidence#
Datadog query 1 — 본 클러스터의 원본 에러:
service:cupixworks-capture-refinement-arm-instance status:error @environment:production "Process killed by signal SIGABRT (code: null)"
주변 로그 (from Datadog service:cupixworks-capture-refinement-arm-instance, 2026-07-14T21:08:30Z–21:15:00Z):
2026-07-15 06:08:20 info RefinementService::init
2026-07-15 06:08:20 info RefinementService::authenticate | begin
2026-07-15 06:08:20 info CupixAuth::setSession | session_id: 31e2e0c9afbc49c302b74163c3d7d72791b9262a
2026-07-15 06:08:20 info RefinementService::run | begin
2026-07-15 06:08:20 info RefinementService::authenticate | end
2026-07-15 06:08:20 info JobManager::loadJob | begin - job id: 1196597
2026-07-15 06:08:20 info JobManager::loadJob | end - job id: 1196597
2026-07-15 06:08:24 warn [123475.07] | video filepath not found - 694025.insv (× 수십 회 반복)
2026-07-15 06:08:24 warn [123474.95] | video filepath not found - 694025.insv (× 수십 회 반복)
2026-07-15 06:08:53 warn [122204.00] | Failed unstitch refinement - landmark visible count decreased. Revert to the stitched state.
2026-07-15 06:09:09 error ChildProcessManager::setupEventHandlers | Child process closed
2026-07-15 06:09:09 error ChildProcessManager::setupEventHandlers | Child process exited
2026-07-15 06:09:09 error Process killed by signal SIGABRT (code: null)
2026-07-15 06:09:09 warn ScenemapperManager::execute | error - {name:'Error', message:'Process killed by signal SIGABRT (code: null)', stack:'... at ChildProcess.<anonymous> (/tmp/agent/dist/app.cjs:5248:33) ...'}
2026-07-15 06:09:10 info RefinementService::terminateService | force shutdown after 10 seconds
2026-07-15 06:11:34 info RefinementService::init ← 새 인스턴스의 다음 job
video filepath not found - 694025.insv 로그와 Failed unstitch refinement - landmark visible count decreased 로그는 소스 코드에는 존재하지 않는 문자열로, native scenemapper_api.node 내부에서 출력하는 것으로 확인 (repo 전체 grep 결과 없음). 이는 abort 직전의 native 실행 상태를 나타낸다.
Datadog query 2 — 14일간 SIGABRT recurrence:
service:cupixworks-capture-refinement-arm-instance "SIGABRT"
결과: 2026-07-02 ~ 2026-07-15 사이 20건 이상. 스택 트레이스가 app.cjs:5307:33에서 app.cjs:5248:33으로 이동한 것으로 보아 그 사이 재배포가 있었으나 문제는 지속.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Native scenemapper_api.node 내부에서 abort() 발생 (C++ assertion / uncaught exception / OOM), 특정 입력(landmark 감소 unstitch revert 경로 + missing .insv 파일)에서 재현 |
scenemapper.process.ts:47 에서 Refiner.process() 호출; abort 직전 native 로그 "Failed unstitch refinement - landmark visible count decreased" 와 대량의 "video filepath not found - 694025.insv" warn; 스택이 항상 ChildProcess.<anonymous> (부모 close handler)로 끝남 → JS 예외가 아님; SIGABRT signal name (JS에서 자발적으로 보내지 않음) |
— | Confirmed |
| H2 | 부모 프로세스가 timeout으로 자식을 SIGKILL/SIGTERM 한 것 | child-process.manager.ts:219-221 에 timeout 시 SIGKILL 로직 존재 |
관측된 signal은 SIGABRT 이지 SIGKILL/SIGTERM 아님; ScenemapperManager 는 execute 에 timeout 옵션을 전달하지 않음 (ChildProcessOptions.executeTimeoutMs 미설정) |
Rejected |
| H3 | Node.js uncaughtException/unhandledRejection 핸들러가 process.exit(1) 을 호출한 것 |
app.ts:42-52 에 두 핸들러 존재 |
이 경로는 exit code 1, signal null 로 관측되어야 함. 실제 로그는 signal=SIGABRT, code=null → 자식이 시그널로 죽음 |
Rejected |
| H4 | 컨테이너 OOM Killer (cgroup)에 의한 종료 | ARM instance 환경, 대용량 입력 처리 | OOM은 통상 SIGKILL. SIGABRT는 프로세스 자신이 raise(SIGABRT) (즉 abort())로 보내는 시그널 → 커널이 아닌 프로세스 내부 발신 |
Rejected |
| H5 | 입력 데이터 손상(video filepath not found)이 실패의 유일한 원인 | 크래시 직전 대량 warn video filepath not found - 694025.insv |
동일 warn 이후에도 native 코드가 "Revert to the stitched state" 로 회복을 시도(=warn 레벨). 크래시는 그 다음 단계에서 별개로 발생 → 입력 손상은 트리거 조건일 수는 있으나 abort 자체의 원인은 native 코드의 미처리 실패 경로 | Contributory (not sole cause) |
Fix Recommendation#
즉시 조치 (Critical)#
- 네이티브 팀에 native crash 조사 이관. 표면적으로 TypeScript agent 레이어에 fix 여지가 거의 없음 — 부모 프로세스는 이미 close/exit/error 이벤트를 모두 로깅하고 pending promise를 reject하며 job 실패 처리를 하고 있음. 진짜 fix는
scenemapper_api.node(native C++ scenemapper 라이브러리)에서 이루어져야 함. 관측된 native 로그 패턴 (landmark visible count decreased+video filepath not found - 694025.insv)을 티켓에 첨부하여 재현 케이스로 전달할 것. - 자식 프로세스에서 core dump 또는 native stack 수집 활성화 고려. 현재 자식 프로세스 stdio 는
inherit기본값이고 (child-process.manager.ts:44-47), pipe 모드가 아니면 native 라이브러리의 stderr 출력을 별도로 캡처하지 못함.ChildProcessOptions.stdioMode: 'pipe'로 refinement agent 도 전환하면 native abort message (예:terminate called after throwing an instance of ...)를 로거로 흘려보낼 수 있어 root cause 파악에 유리.- 관련:
child-process.manager.ts:138-141에 이미 stderr handler 존재하나 pipe 모드일 때만 동작. - 자동화된 code-fix 대상에서는 제외 권장 — 다른 agent (skat, thumbnail, mesh 등)와 동작이 달라지므로 lemoine 팀 리뷰 필요.
- 관련:
단기 개선 (1주 이내)#
- 재시도 정책 도입 검토. 현재
RefinementService::runcatch 블록(refinement-service.ts:107-110)은 즉시 job을 error로 마킹함. 동일 job을 새 인스턴스에서 1회 재시도하는 정책이 있는지 lemoine 팀과 확인. 없다면 native abort의 transient 케이스 (예: 특정 image frame corruption)에 대해 상위 job 재큐 로직 검토. - 입력 검증 강화.
RefinementService::checkInputFiles(refinement-service.ts:144-154) 는alignment_archive.bin만 검증. 크래시 트리거로 지목된.insvvideo files 존재 여부를 native 호출 전에 검증해 실패 경로를 결정적으로 만들 것. - 동일 fingerprint 재발 알림. SIGABRT recurrence 를 지표화하여 (하단 Monitoring 섹션 참조) 특정 threshold 초과 시 team notify.
장기 개선 (재발 방지)#
- Native 라이브러리 crash 관찰가능성 향상: refinement agent 컨테이너에 core dump 저장 볼륨과
gdb/addr2line을 갖춘 debug image 배포 옵션. Kubernetes/ECS taskdefinition 에서ulimit -c unlimited활성화. - Native ↔ Node 경계 계약 표준화:
Refiner.process()실패 시 native 측이 예외 대신 result code 를 반환하도록 인터페이스 재설계 → JS 레벨에서 결정적 실패 경로 확보. - Refinement 입력 pre-validation 파이프라인: 상류 (skat master / postprocessor) 산출물에 대해
.insvpresence + landmark count 정합성을 사전 검사하는 gate 를 도입해 이상 데이터가 refinement 로 넘어오지 않게 함.
Monitoring#
- SIGABRT 발생 카운트 timeseries — refinement agent 전반의 SIGABRT recurrence 추적:
service:cupixworks-capture-refinement-arm-instance status:error "SIGABRT"
- Child process close (비정상 종료) 카운트 — SIGABRT 외 다른 시그널까지 포괄:
service:cupixworks-capture-refinement-arm-instance "Child process closed"
- native precursor warn 로그 카운트 — abort 직전 native 상태 신호:
service:cupixworks-capture-refinement-arm-instance status:warn "Failed unstitch refinement"
- RefinerExecute 에러 코드 발생 카운트 — job-level 실패 추적 (tesla 측 job 테이블
error_code = 'AGT3012'대비):
service:cupixworks-capture-refinement-arm-instance "ScenemapperManager::execute | error"
임계치 제안: SIGABRT 카운트가 24h 롤링 윈도우에서 5건을 초과하면 lemoine 팀 채널로 알림.
Risk Assessment#
- Risk level: medium — 서비스 자체는 job 단위 리사이클로 회복하며, 부모 프로세스의 예외 처리 경로는 이미 정상 동작. 다만 native crash 는 개별 refinement job 실패로 이어져 사용자 capture 산출물이 미생성되며 14일에 20+회 재발하는 만성 이슈.
- 예상 복잡도: critical (native scenemapper C++ 라이브러리 수정 필요). TypeScript agent 레이어에서 완결 가능한 fix는 관찰가능성/입력 검증 개선 정도로 제한적.