PixGenieProcess | process error: The operation was aborted
RCA: PixGenieProcess | process error: The operation was aborted
Overview#
What Happened#
2026-07-02 22:52 KST 에 cupixworks-pano-postprocessor-instance 서비스에서 tenant cana capture 를 처리하던 4개 인스턴스가 동시에 PixGenieProcess | process error: The operation was aborted 로그를 남기고 실패했다. 이 로그는 pixgenie.process.ts:116 의 child process 'error' 이벤트 핸들러에서 발생하며, 원인은 동일 파일 line 18 에 하드코딩된 TIMEOUT_MS = 2 * 60 * 60 * 1000 (2 시간) AbortSignal.timeout 이 만료되어 _abortController.abort(new Error('Process timeout')) 가 호출되었기 때문이다. 본 클러스터는 자매 클러스터 608b2052-c042-4bec-afcd-052a31b82fd0 (PixGenieProcess::execute | failed - {"code":"ABORT_ERR","name":"AbortError"}) 과 동일한 abort 이벤트에서 함께 발생한다 — 하나의 abort 가 error 리스너와 execute() catch 블록 두 곳에서 각각 로깅된다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | AbortError |
| exception.message | The operation was aborted |
| top_frame | packages/cupix-pix-genie-preprocessor-agent/src/process/pixgenie.process.ts:116 |
| runtime | Node.js child_process (spawn python3) on Ubuntu 22.04 |
| env | production, us-west-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
cana (tenant cupix) |
4 | PixGenie 전처리 대상 capture 의 처리 중단 — 자매 클러스터 608b2052 에서 확인된 capture 724274 와 동일한 abort 사건으로 판단됨 |
Timeline#
- 2026-07-02 20:52:33 KST —
PixGenieProcess::execute | begin(4개 인스턴스가 동시에 Python 자식 프로세스 spawn; UTC 11:52:33) - 2026-07-02 22:52:33 KST — 2 시간 경과,
timeoutSignal발화 →logger.warn('PixGenieProcess | process timeout, aborting...')(4건) - 2026-07-02 22:52:33 KST —
_abortController.abort(new Error('Process timeout'))호출 → Node.js 가 자식 프로세스에 SIGTERM 발송 - 2026-07-02 22:52:33 KST — ChildProcess
'error'이벤트 발화 →logger.error('PixGenieProcess | process error: %s', error.message)→ 본 클러스터 대표 에러 로그 (4건) - 2026-07-02 22:52:33 KST — 동일 abort 로
spawnPromise 가AbortErrorreject → 상위execute()catch 가PixGenieProcess::execute | failed - {"code":"ABORT_ERR","name":"AbortError"}로그 발생 (자매 클러스터608b2052)
Error Log#
PixGenieProcess | process error: The operation was aborted
Impact#
- Service:
cupixworks-pano-postprocessor-instance - Team: cana
- 발생 횟수: 4
- 최초 발생: 2026-07-02 22:52 KST
- 최근 발생: 2026-07-02 22:52 KST
4건 모두 동일 시각(22:52:33 KST)에 발생했으며, warn 레벨의 PixGenieProcess | process timeout, aborting... 로그도 동일 시각에 정확히 4건이 관찰된다. 즉 하나의 abort 이벤트가 4개 인스턴스 각각에서 한 번씩 발생한 단일 사건이며, 자매 클러스터 608b2052 의 4건과 함께 총 8개의 error 로그가 4개의 원인(=aborted process) 에서 파생됐다. 최근 14일 내 다른 발생 이력: 2026-06-21 19:39 KST (3건), 2026-06-20 08:04 KST (1건) — 하드코딩된 2 시간 상한이 주기적으로 재발한다는 신호.
Root Cause Summary#
본 클러스터의 에러 로그는 pixgenie.process.ts:116 — spawn 으로 띄운 Python 자식 프로세스의 'error' 이벤트 핸들러에서 발생한다. Node.js 는 spawn(..., { signal }) 로 넘긴 AbortController.signal 이 abort 되면 자식 프로세스에 SIGTERM 을 보낸 뒤 ChildProcess 인스턴스에 AbortError 로 'error' 이벤트를 emit 한다. 트리거는 pixgenie.process.ts:56 에서 설정한 AbortSignal.timeout(2 * 60 * 60 * 1000) 이며, 2 시간이 지나면 timeoutSignal 이 발화하여 _abortController.abort(new Error('Process timeout')) 를 호출한다 (line 60). 즉 이 클러스터는 "PixGenie 처리 시간이 하드코딩된 2 시간 타임아웃을 초과했다" 가 근본 원인이며, 자매 클러스터 608b2052-c042-4bec-afcd-052a31b82fd0 (ABORT_ERR / AbortError) 과 동일한 abort 이벤트의 다른 로그 지점에 불과하다. 로그 메시지가 The operation was aborted 로만 표시되고 실제 원인("Process timeout") 이 사라진 것은 error.message 만 포맷하는 로거 호출 방식의 부수적 관찰성 이슈다.
Technical Analysis#
Code Path#
- Entry:
pixgenie.process.ts:24(PixGenieProcess.execute) — 호출자는PixGenieManager를 통해PreprocessorService::run에서 진입 - Timeout 설정:
pixgenie.process.ts:52-61(runPythonProcess내AbortSignal.timeout등록) - Spawn 호출:
pixgenie.process.ts:65-70—spawn('python3', ..., { signal: this._abortController.signal }) - Failure point (본 클러스터 로그):
pixgenie.process.ts:115-118— ChildProcess'error'리스너 - 동시 발생 (자매 클러스터 로그):
pixgenie.process.ts:36-39—execute()catch 블록의JSON.stringify(error)
Timeout 등록 로직:
private runPythonProcess(params: PixGenieProcessParams): Promise<void> {
return new Promise((resolve, reject) => {
this._abortController = new AbortController();
const timeoutSignal = AbortSignal.timeout(this.TIMEOUT_MS);
timeoutSignal.addEventListener('abort', () => {
logger.warn('PixGenieProcess | process timeout, aborting...');
this._abortController!.abort(new Error('Process timeout'));
}, { once: true });
logger.debug('PixGenieProcess::runPythonProcess | executing: python3 %s', PixGenieProcess.PIXGENIE_SCRIPT_PATH);
this._process = spawn('python3', [PixGenieProcess.PIXGENIE_SCRIPT_PATH], {
env: { ...process.env, ...this.buildEnvironment(params) },
stdio: ['ignore', 'pipe', 'pipe'],
cwd: params.workspaceDir,
signal: this._abortController.signal
});
this.setupProcessHandlers(resolve, reject);
});
}
하드코딩된 2 시간 상수:
private readonly TIMEOUT_MS = 2 * 60 * 60 * 1000; // 2 hours
본 클러스터 로그가 발생하는 정확한 지점 (child process 'error' 이벤트):
this._process.on('error', (error) => {
logger.error('PixGenieProcess | process error: %s', error.message);
reject(error);
});
기대 동작: Python 자식 프로세스가 정상 실행 후 'close' 이벤트에서 exit code 0 으로 resolve() (line 108-109).
실제 동작: 2 시간 시점에 AbortSignal.timeout 이 발화 → _abortController.abort(new Error('Process timeout')) → Node.js child_process 가 signal 옵션에 반응해 SIGTERM 을 보내고 ChildProcess 에 AbortError(code: ABORT_ERR, message: 'The operation was aborted') 로 'error' 이벤트 emit → 본 클러스터의 logger.error('PixGenieProcess | process error: %s', error.message) 실행. reject(error) 로 인해 상위 execute() catch 도 실행되어 자매 클러스터 608b2052 의 execute | failed - {"code":"ABORT_ERR",...} 로그가 함께 남는다.
Log Evidence#
Datadog 쿼리 (본 클러스터 대표 로그 재현):
service:cupixworks-pano-postprocessor-instance status:error @environment:production "PixGenieProcess"
동일 시각(22:52:33 KST)에 나타난 8개 error 로그 (본 클러스터 4건 + 자매 클러스터 4건):
[2026-07-02 22:52:33] error PixGenieProcess::execute | failed - {"code":"ABORT_ERR","name":"AbortError"}
[2026-07-02 22:52:33] error PixGenieProcess::execute | failed - {"code":"ABORT_ERR","name":"AbortError"}
[2026-07-02 22:52:33] error PixGenieProcess::execute | failed - {"code":"ABORT_ERR","name":"AbortError"}
[2026-07-02 22:52:33] error PixGenieProcess::execute | failed - {"code":"ABORT_ERR","name":"AbortError"}
[2026-07-02 22:52:33] error PixGenieProcess | process error: The operation was aborted
[2026-07-02 22:52:33] error PixGenieProcess | process error: The operation was aborted
[2026-07-02 22:52:33] error PixGenieProcess | process error: The operation was aborted
[2026-07-02 22:52:33] error PixGenieProcess | process error: The operation was aborted
Abort 트리거 증거 (warn 레벨의 timeoutSignal 발화 로그) — 정확히 동일 시각에 4건:
service:cupixworks-pano-postprocessor-instance @environment:production "PixGenieProcess | process timeout"
[2026-07-02 22:52:33] warn PixGenieProcess | process timeout, aborting...
[2026-07-02 22:52:33] warn PixGenieProcess | process timeout, aborting...
[2026-07-02 22:52:33] warn PixGenieProcess | process timeout, aborting...
[2026-07-02 22:52:33] warn PixGenieProcess | process timeout, aborting...
동일 로직으로 재발한 과거 사건 (지난 14일):
[2026-06-21 19:39:39] warn PixGenieProcess | process timeout, aborting... (3건)
[2026-06-20 08:04:22] warn PixGenieProcess | process timeout, aborting... (1건)
자매 클러스터 608b2052 의 상위 PreprocessorService::run 스택 (동일 abort 이벤트, 완전한 cause 포함):
{
"stack": "AbortError: The operation was aborted\n at abortChildProcess (node:child_process:725:27)\n at EventTarget.onAbortListener (node:child_process:795:7)\n at AbortController.abort (node:internal/abort_controller:392:5)\n at timeoutSignal.addEventListener.once (/tmp/agent/dist/app.cjs:6613:31)",
"message": "The operation was aborted",
"cause": {
"message": "Process timeout",
"name": "Error"
},
"code": "ABORT_ERR",
"name": "AbortError"
}
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | 하드코딩된 2 시간 AbortSignal.timeout 이 만료되어 Node 가 SIGTERM 을 보냈고, ChildProcess 'error' 이벤트가 AbortError 로 emit 되어 pixgenie.process.ts:116 이 로그를 남김 |
동일 시각에 `PixGenieProcess | process timeout, aborting...warn 로그 4건; 자매 클러스터의 상위 stack 이abortChildProcess→timeoutSignal.addEventListener.once로 이어짐;cause: { message: "Process timeout" }` |
— |
| H2 | Python 프로세스가 자체 크래시로 exit code ≠ 0 종료 → 'close' 리스너에서 reject(new Error('PixGenie process failed with exit code ...')) 발생 |
— | 해당 메시지가 로그에 없음; 대신 The operation was aborted (AbortError) 가 발생. code === 0 이 아닌 close 는 line 111 로 흐르며 별도 메시지를 남기게 되어 있음 |
Rejected |
| H3 | 본 클러스터와 자매 클러스터 608b2052 는 서로 다른 원인의 별도 사건 |
— | 두 클러스터 모두 정확히 4건, 정확히 동일 시각(22:52:33 KST, ms 차이 0.001s), 동일 first_seen. 코드상 하나의 abort 는 'error' 리스너(line 115) 와 execute() catch(line 36) 두 지점에서 각각 로깅됨 |
Confirmed as duplicate log surface of same event |
| H4 | 상위 서비스 shutdown/terminateService 로 인한 abort |
— | terminateService 는 PreprocessorService::run 종료 이후 시작되는 별도 forced-shutdown 타이머로, 2 시간 시점의 abort 와 시점 및 스택이 일치하지 않음 (자매 클러스터 RCA 에서 이미 배제) |
Rejected |
| H5 | 로그 메시지가 The operation was aborted 로만 나오는 것은 logger.error('... %s', error.message) 가 AbortError.message 만 포맷하기 때문 (cause 손실) |
pixgenie.process.ts:116 은 error.message 만 인자로 넘김; AbortError.cause = Error('Process timeout') 는 로그에서 사라짐 |
상위 PreprocessorService::run 이 완전한 stack/cause 를 별도로 남기므로 전체 정보 손실은 아님 |
Confirmed (secondary — 관찰성 개선 필요) |
Fix Recommendation#
즉시 조치 (Critical)#
- 본 클러스터는 자매 클러스터
608b2052-c042-4bec-afcd-052a31b82fd0의 duplicate log surface 이므로 별도 수정 아이템을 만들 필요는 없다. 근본 대응은 자매 클러스터의 RCA (content/docs/incidents/608b2052-c042-4bec-afcd-052a31b82fd0/rca.mdx) 에 정의된 방향(2 시간 하드코딩 재평가 + 실제 capture 처리 시간 조사) 을 따른다. - Deduplication 관점의 즉시 조치:
pixgenie.process.ts:116의'error'리스너는reject(error)만 하고 로그를 남기지 않도록 정리하는 방향을 검토한다. 상위execute()catch 가 이미 동일 error 를 로깅하므로 line 116 의 log 는 하나의 실패에 대해 두 개의 error 로그를 만드는 중복 지점이다.
단기 개선 (1주 이내)#
- 로그 중복 제거:
'error'리스너에서logger.error(...)를 삭제하거나, 반대로 상위execute()catch 의JSON.stringify(error)로그를 유일한 error surface 로 삼는다. 파일:packages/cupix-pix-genie-preprocessor-agent/src/process/pixgenie.process.ts:115-118. - 관찰성 수정 (
pixgenie.process.ts:37,:116공통 문제):error.message/JSON.stringify(error)대신 팀 표준 로거 직렬화(예:stringifyError—preprocessor-service.ts에서 이미 사용) 를 사용해cause(Process timeout) 까지 로그에 포함되게 한다. 현재는 대표 에러 로그만으로 원인이 "timeout" 임을 확인할 수 없다. - 타임아웃 상수 외부화:
TIMEOUT_MS를 환경변수(예:PIXGENIE_TIMEOUT_MS) 로 노출해 코드 배포 없이 조정 가능하도록 한다 (자매 클러스터 RCA 와 동일 권고).
장기 개선 (재발 방지)#
- 자매 클러스터 RCA 의 장기 권고(재시도/재개 checkpoint, capture-size 기반 동적 타임아웃, 무진행 heartbeat 기반 abort) 를 그대로 적용한다.
- 클러스터링 개선 (error-sweeper 측): 동일 abort 이벤트가 서로 다른 로그 지점에서 두 개의 클러스터로 분리되지 않도록 fingerprint 규칙 재검토 — 현재
PixGenieProcess | process error: %s와PixGenieProcess::execute | failed - %s가 별도 fingerprint 로 분리된다.
Monitoring#
- PixGenie 2 시간 타임아웃 발생 카운트 — 하드코딩된 상한이 재발하는지 추적:
service:cupixworks-pano-postprocessor-instance @environment:production "PixGenieProcess | process timeout"
- 본 클러스터의 로그 지점 카운트 — deduplication 이후 0 이 되어야 정상:
service:cupixworks-pano-postprocessor-instance @environment:production status:error "PixGenieProcess | process error"
- PixGenie 성공 대비 실패 비율 — abort 발생률 트렌드:
service:cupixworks-pano-postprocessor-instance @environment:production "PixGenieProcess::execute" ("completed successfully" OR "failed")
Risk Assessment#
- Risk level: low — 자매 클러스터
608b2052와 동일 abort 이벤트에서 파생된 duplicate log surface. 사용자 영향은 자매 클러스터의 impact 로 이미 커버됨. - 예상 복잡도: trivial —
pixgenie.process.ts:115-118의 로그 지점 정리 또는 로거 직렬화 교체. 근본적인 2 시간 타임아웃 문제는 자매 클러스터 트랙에서 처리.