ChildProcessManager::setupEventHandlers | Child process stderr: AssertionError: Assertion failed - I
RCA: Invalid OTG Header in SVF2 Geometry Loading
Overview#
What Happened#
2026-05-06 13:33~14:03 UTC 사이에 cupixworks-any-bimrevision-agent 서비스에서 SVF2 형식의 BIM geometry 데이터를 로딩하는 과정에서 AssertionError: Assertion failed - Invalid OTG header 에러가 8회 발생했다. 에러는 Autodesk APS(forge)에서 다운로드한 geometry buffer의 매직 헤더 검증 실패로 인해 발생했으며, 모든 케이스에서 10초 후 재시도 시 성공하여 최종 작업에는 영향이 없었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | AssertionError |
| exception.message | Assertion failed - Invalid OTG header |
| top_frame | @cupix/forge-utils/convert-utils/svf2/svf2-geometry-wss-loader.js:436 |
| runtime | Node.js 20 |
| env | production, us-west-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| wgyates / BIM Revision | 8 | 로그 노이즈 (재시도 성공으로 기능 영향 없음) |
Timeline#
- 2026-05-06T13:33:47Z — 최초 에러 발생 (geometry hash:
뻬粕鰏딫겙䄛诣蕝퀢,processTargetAndFillReport) - 2026-05-06T13:33:57Z — 동일 hash 재시도 성공 (attempt 2)
- 2026-05-06T13:55:01Z~13:55:12Z — 추가 에러 2건 (
collectPrevEntities) - 2026-05-06T14:03:45Z — 마지막 에러 발생, 10초 후 재시도 성공
- 2026-05-06T14:03:56Z — 모든 geometry 로딩 재시도 성공 완료
Error Log#
ChildProcessManager::setupEventHandlers | Child process stderr: AssertionError: Assertion failed - Invalid OTG header
at Object.assert (/tmp/agent/dist/node_modules/@cupix/forge-utils/cpassert.js:30:19)
at GeomDecoder.read (/tmp/agent/dist/node_modules/@cupix/forge-utils/convert-utils/svf2/svf2-geometry-wss-loader.js:436:33)
at SVF2GeometryWSSLoader.parseGeometryResource (/tmp/agent/dist/node_modules/@cupix/forge-utils/convert-utils/svf2/svf2-geometry-wss-loader.js:258:37)
at SVF2GeometryWSSLoader.parseWithRetry (/tmp/agent/dist/node_modules/@cupix/forge-utils/convert-utils/svf2/svf2-geometry-wss-loader.js:139:33)
at SVF2GeometryWSSLoader.runOneAttempt (/tmp/agent/dist/node_modules/@cupix/forge-utils/convert-utils/svf2/svf2-geometry-wss-loader.js:126:28)
at async SVF2GeometryWSSLoader.loadResources (/tmp/agent/dist/node_modules/@cupix/forge-utils/convert-utils/svf2/svf2-geometry-wss-loader.js:100:13)
at async SVF2GeometryWSSLoader.load (/tmp/agent/dist/node_modules/@cupix/forge-utils/convert-utils/svf2/svf2-geometry-wss-loader.js:68:9)
at async SVF2Reader.loadGeometries (/tmp/agent/dist/node_modules/@cupix/forge-utils/convert-utils/svf2/svf2-reader.js:170:36)
at async RevitEntityMap.loadGeometry (/tmp/agent/dist/node_modules/@cupix/forge-agents/extractor/compare-extractor/entity-map.js:174:32)
at async CompareExtractor.processTargetAndFillReport (/tmp/agent/dist/node_modules/@cupix/forge-agents/extractor/compare-extractor/compare-extractor.js:177:17)
Impact#
- Service:
cupixworks-any-bimrevision-agent - Team: wgyates
- 발생 횟수: 8
- 최초 발생: 2026-05-06T13:33:47.022Z
- 최근 발생: 2026-05-06T14:03:45.341Z
- 기능 영향: 없음 (모든 케이스에서 재시도 성공)
Root Cause Summary#
Autodesk APS(forge) OTG WebSocket 서비스에서 geometry buffer를 다운로드할 때, 첫 번째 시도에서 수신한 데이터가 손상되거나 불완전하여 GeomDecoder.readNodeJS()의 매직 헤더 검증('OTG0')이 실패했다. 로그에서 확인된 패턴은 모든 실패 케이스에서 decompressed size가 0인 것으로, buffer가 truncate되었거나 Autodesk 측 CDN/WebSocket에서 일시적으로 잘못된 응답(에러 페이지, 빈 응답)을 반환한 것으로 판단된다. @cupix/forge-utils의 parseWithRetry 메커니즘이 10초 후 재시도하여 모든 케이스에서 성공했으므로, 이 에러는 Autodesk 서비스의 일시적 불안정(transient failure)이 원인이다.
Technical Analysis#
Code Path#
- Entry point:
bim-revision-service.ts:177—runBimCompare()호출 BimCompareManager.execute()→ child process IPC →BimCompareProcess.execute()→ForgeAgent.extract()ForgeAgent.extract()→CompareExtractor.processTargetAndFillReport()→RevitEntityMap.loadGeometry()SVF2Reader.loadGeometries()→SVF2GeometryWSSLoader.load()→loadResources()→runOneAttempt()→parseWithRetry()- Failure point:
svf2-geometry-wss-loader.js:436—GeomDecoder.readNodeJS()
GeomDecoder.readNodeJS() 메서드에서 buffer의 처음 4바이트가 'OTG0' 매직 문자열과 일치하는지 검증한다:
readNodeJS() {
try {
const buffer = Buffer.from(this.buffer_);
const magic = buffer.toString('ascii', 0, 4);
cpassert_1.CPAssert.assert(magic === 'OTG0', 'Invalid OTG header'); // LINE 435: FAILS HERE
this.readOffset_ = 4;
기대 동작: Autodesk APS에서 다운로드한 geometry buffer의 첫 4바이트는 'OTG0'이어야 한다.
실제 동작: 첫 번째 다운로드 시도에서 buffer 내용이 올바르지 않아 assertion 실패. decompressed size가 0으로 기록됨.
ChildProcessManager는 child process의 stderr 출력을 캡처하여 error 레벨로 로깅한다:
this.process.stderr?.on('data', (data: Buffer) => {
const text = data.toString();
logger.error(`ChildProcessManager::setupEventHandlers | Child process stderr: ${text}`);
});
이것은 child process 내부에서 발생한 AssertionError가 stderr로 출력된 것을 parent process가 캡처하여 error로 로깅한 것이다. 실제로는 parseWithRetry 로직이 이 에러를 catch하고 10초 후 재시도하여 성공한다.
Log Evidence#
Datadog 검색 쿼리:
service:cupixworks-any-bimrevision-agent status:error @environment:production "Invalid OTG header"
4건의 "Invalid OTG header" 에러 발생 후 각각 "Failed to decode geometry buffer" 에러가 쌍으로 기록됨:
2026-05-06T14:03:45.341Z | Failed to decode geometry buffer - id: 1333, hash: 嫑㊉ಛ㬑笴郕谂迃곴, rawSize: 3571 (size: 0, zip: T), buffer: 333 ~ 3904
2026-05-06T13:55:12.521Z | Failed to decode geometry buffer - id: 854, hash: ㈳쮡襄娅✭饩힔, rawSize: 2221 (size: 0, zip: T), buffer: 1683 ~ 3904
2026-05-06T13:55:01.705Z | Failed to decode geometry buffer - id: 593, hash: 梞霨톩믮ᆬᾺ𥳐, rawSize: 938 (size: 0, zip: T), buffer: 822 ~ 1760
2026-05-06T13:33:47.023Z | Failed to decode geometry buffer - id: 963, hash: 뻬粕鰏딫겙䄛诣蕝퀢, rawSize: 8156 (size: 0, zip: T), buffer: 36 ~ 8192
핵심 관찰: 모든 케이스에서 size: 0 (decompressed size)이지만 rawSize는 0이 아님. 이는 buffer를 받았으나 내용이 유효한 OTG 데이터가 아님을 의미한다.
재시도 성공 로그:
2026-05-06T13:33:57.727Z | Successfully parsed geometry resource for hash 뻬粕鰏딫겙䄛诣蕝퀢, attempt 2
2026-05-06T13:55:12.498Z | Successfully parsed geometry resource for hash 梞霨톩믮ᆬᾺ𥳐, attempt 2
2026-05-06T13:55:23.486Z | Successfully parsed geometry resource for hash ㈳쮡襄娅✭饩힔, attempt 2
2026-05-06T14:03:56.023Z | Successfully parsed geometry resource for hash 嫑㊉ಛ㬑笴郕谂迃곴, attempt 2
처리 컨텍스트:
Queue: cupix-tesla-bim-revision-agent-production (SQS, us-west-2)
Facility: ercgqs
Models: TRADE_400E_CRDR, TRADE_400F_CRDR, TRADE_400C_CRDR, TRADE_400A_CRDR
Format: SVF2, Region: US
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Autodesk APS WebSocket 서비스의 일시적 불안정으로 geometry buffer가 손상/불완전하게 전달됨 | 모든 4건에서 decompressed size=0이나 rawSize>0; 10초 후 재시도 시 동일 hash 100% 성공; 특정 시간대(30분)에 집중 발생 | — | Confirmed |
| H2 | 로컬 메모리 부족으로 인한 buffer 할당 실패 | 동일 시간대에 여러 geometry가 동시 처리됨 | OOM이면 Node.js crash가 발생해야 하나 프로세스는 정상 동작; rawSize가 0이 아닌 데이터가 수신됨; 재시도 성공은 메모리 문제와 무관 | Rejected |
| H3 | SVF2 파일 자체의 형식 오류 (Autodesk 변환 실패) | — | 재시도 시 동일 hash로 성공함. 파일 자체 오류면 재시도도 실패해야 함 | Rejected |
| H4 | 네트워크 패킷 손실로 buffer truncation | buffer가 수신되었으나 content가 invalid (size=0 after decompression) | rawSize는 정상 범위 값이므로 TCP 레벨 truncation은 아님; 더 가능성 있는 것은 CDN 에러 응답이 geometry 대신 반환된 경우 | Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
- 조치 불필요: 현재
parseWithRetry재시도 로직이 정상 동작하여 모든 케이스에서 복구됨. 기능적 영향 없음.
단기 개선 (1주 이내)#
ChildProcessManager의 stderr 캡처 로그 레벨을error에서warn으로 변경 검토 (child-process.manager.ts:140). 현재는 child process의 모든 stderr 출력이 무조건error레벨로 기록되어 불필요한 알림을 유발한다.@cupix/forge-utils의 assertion 실패는 내부적으로 retry되어 복구되는 케이스이므로, stderr 자체가 반드시 서비스 에러를 의미하지 않는다.- 대안:
parseWithRetry에서 retry 성공 시 이전 실패를warn레벨로 기록하고 stderr 출력을 억제하도록@cupix/forge-utils패키지 수정 요청.
장기 개선 (재발 방지)#
@cupix/forge-utils의GeomDecoder.readNodeJS()에서 assertion 실패 시throw대신 structured error를 반환하고,parseWithRetry가 이를 gracefully 처리하도록 리팩터링. 이를 통해 retry 가능한 에러가 stderr로 누출되지 않게 할 수 있다.- Autodesk APS geometry WebSocket의 응답 실패율을 모니터링하는 메트릭 추가.
Monitoring#
- Autodesk APS geometry 로딩 retry 비율 메트릭 추가:
service:cupixworks-any-bimrevision-agent "Successfully parsed geometry resource" "attempt 2"
- stderr assertion 실패 빈도 추적 (현재 대비 급증 시 알림):
service:cupixworks-any-bimrevision-agent status:error "Invalid OTG header"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (로그 레벨 변경만 필요, 기능 수정 불필요)