ES /docs

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#

  1. 2026-05-06T13:33:47Z — 최초 에러 발생 (geometry hash: 뻬粕鰏딫겙䄛诣蕝퀢, processTargetAndFillReport)
  2. 2026-05-06T13:33:57Z — 동일 hash 재시도 성공 (attempt 2)
  3. 2026-05-06T13:55:01Z~13:55:12Z — 추가 에러 2건 (collectPrevEntities)
  4. 2026-05-06T14:03:45Z — 마지막 에러 발생, 10초 후 재시도 성공
  5. 2026-05-06T14:03:56Z — 모든 geometry 로딩 재시도 성공 완료

Error Log#

Datadog Logs

text
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-utilsparseWithRetry 메커니즘이 10초 후 재시도하여 모든 케이스에서 성공했으므로, 이 에러는 Autodesk 서비스의 일시적 불안정(transient failure)이 원인이다.

Technical Analysis#

Code Path#

  • Entry point: bim-revision-service.ts:177runBimCompare() 호출
  • 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:436GeomDecoder.readNodeJS()

GeomDecoder.readNodeJS() 메서드에서 buffer의 처음 4바이트가 'OTG0' 매직 문자열과 일치하는지 검증한다:

@cupix/forge-utils/convert-utils/svf2/svf2-geometry-wss-loader.js:431-436javascript
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 레벨로 로깅한다:

packages/base/src/manager/child-process.manager.ts:138-141typescript
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 검색 쿼리:

text
service:cupixworks-any-bimrevision-agent status:error @environment:production "Invalid OTG header"

4건의 "Invalid OTG header" 에러 발생 후 각각 "Failed to decode geometry buffer" 에러가 쌍으로 기록됨:

text
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 데이터가 아님을 의미한다.

재시도 성공 로그:

text
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

처리 컨텍스트:

text
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-utilsGeomDecoder.readNodeJS()에서 assertion 실패 시 throw 대신 structured error를 반환하고, parseWithRetry가 이를 gracefully 처리하도록 리팩터링. 이를 통해 retry 가능한 에러가 stderr로 누출되지 않게 할 수 있다.
  • Autodesk APS geometry WebSocket의 응답 실패율을 모니터링하는 메트릭 추가.

Monitoring#

  • Autodesk APS geometry 로딩 retry 비율 메트릭 추가:
text
service:cupixworks-any-bimrevision-agent "Successfully parsed geometry resource" "attempt 2"
  • stderr assertion 실패 빈도 추적 (현재 대비 급증 시 알림):
text
service:cupixworks-any-bimrevision-agent status:error "Invalid OTG header"

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial (로그 레벨 변경만 필요, 기능 수정 불필요)