ES /docs

ChildProcessManager::setupEventHandlers | Child process stderr: at Object.assert (/tmp/agent/dis

RCA: ChildProcessManager stderr assertion failure (cpassert.js)

Overview#

What Happened#

2026-05-27 09:15~09:17 UTC 사이에 cupixworks-any-bimrevision-agent 서비스의 BIM comparison child process에서 @cupixapps/forge-utils 패키지의 assertion 실패가 4회 발생했다. 두 가지 유형의 assertion failure가 혼합되어 있으며, 하나는 geometry decode 관련(비치명적, 재시도 후 성공), 다른 하나는 derivative 누락 관련(치명적, 작업 실패)이다.

Quick Facts#

Field Value
exception.class AssertionError / AssertionError [ForgeAgentError]
exception.message Assertion failed - Invalid OTG header / Must not be a nullable value: Derivative not found for modelId: 11
top_frame cpassert.js:30 (geometry) / cpassert.js:27 (derivative)
runtime Node.js child process (/tmp/agent/dist/)
deploy @cupixapps/forge-utils@10.98.0, @cupix/forge-agents@10.21.0
env production, us-west-2

Affected Teams#

Team / Domain Error Count Impact
exyte (BIM Revision) 4 bim_id 18046 revision 실패 (V2→V3 비교 불가), bim_id 17157은 재시도 후 성공

Timeline#

  1. 09:15:13Z — bim_id 17157 (facilityKey 5k6vfs) geometry decode 중 "Invalid OTG header" assertion 발생 (stderr로 출력)
  2. 09:15:13Z — 동일 작업에서 "Failed to decode geometry buffer (raw size: 6220, size: 0)" assertion 발생
  3. 09:15:23Z — bim_id 17157 geometry 재시도 성공 (attempt 2)
  4. 09:17:30Z — bim_id 17157 성공적으로 완료 (2698 entities)
  5. 09:17:56Z — bim_id 18046 (facilityKey 5k6vfs) "Derivative not found for modelId: 11" assertion 2회 발생, 작업 실패

Error Log#

Datadog Logs

text
ChildProcessManager::setupEventHandlers | Child process stderr:     at Object.assert (/tmp/agent/dist/node_modules/.pnpm/@cupixapps+forge-utils@10.98.0_axios@1.16.1_commander@3.0.2_fflate@0.8.3_rxjs@7.8.2_three@0.170.0_ws@8.21.0/node_modules/@cupixapps/forge-utils/cpassert.js:30:19)

Impact#

  • Service: cupixworks-any-bimrevision-agent
  • Team: exyte
  • 발생 횟수: 4
  • 최초 발생: 2026-05-27T09:15:13.021Z
  • 최근 발생: 2026-05-27T09:17:56.521Z

Root Cause Summary#

이 에러 클러스터는 두 가지 별개의 assertion failure를 포함한다. 치명적인 근본 원인은 bim_id 18046의 BIM comparison 과정에서 이전 revision(V2)의 Forge derivative를 로드할 때, modelId 11에 해당하는 derivative가 Autodesk Forge 플랫폼에 존재하지 않아 assertDefined가 실패한 것이다. 이 모델("EGI_OG1_18-11-2025_Issued_ACC")은 published: false 상태로 facility에서 제거되었지만, 이전 revision의 entity 목록에는 여전히 이 modelId를 참조하는 엔트리가 남아있어 derivative 로드를 시도하게 된다. @cupixapps/forge-agents 패키지의 RevitEntityMap.load() (entity-map.js:97)에서 derivative 존재를 assertDefined로 검증하기 때문에, 누락 시 graceful fallback 없이 즉시 프로세스가 중단된다.

Technical Analysis#

Code Path#

  1. Entry point: bim-revision-service.ts:603runBimCompare() 호출
src/bim-revision-service.ts:603-638typescript
private runBimCompare = async (cpBimRevision: CPBimRevision, cpPreviousBimRevision: CPBimRevision): Promise<void> => {
    logger.info('BimRevisionService::runBimCompare | begin');
    // ... validation checks ...
    const params: BimComparerParams = {
        apiConfig: { clientId: Environment.ADF_CLIENT_ID, clientSecret: Environment.ADF_CLIENT_SECRET },
        urn: cpBimRevision.forgeUrn,
        previousUrn: cpPreviousBimRevision.forgeUrn,
        query: { entities: cpBim.getBimEntities(), ... }
    };
  1. Child process delegation: bim-compare.manager.ts:53BimCompareManager.execute()ChildProcessManager.execute()
src/manager/bim-compare.manager.ts:53-78typescript
async execute(params: BimComparerParams): Promise<ForgeAgent.Result> {
    try {
        const result = await this.childProcessManager.execute<ForgeAgent.Result>('execute', params);
        return result;
    } catch (error) {
        const errorCode = ErrorCode.Agent.BimRevisionExtractorExecute;
        if (this.setErrorCode) { this.setErrorCode(errorCode); }
        throw error;
    }
}
  1. Child process execution: bim-compare.process.ts:72ForgeAgent.extract() 호출
src/process/bim-compare.process.ts:72-81typescript
const result: ForgeAgent.Result = await ForgeAgent.extract(params.apiConfig, params.urn, {
    region: params.region,
    extractRoom: false,
    extractMeta: false,
    extractCompare: {
        urn: params.previousUrn,
        region: params.previousRegion,
        query: params.query
    }
});
  1. Failure point (external package): entity-map.js:97RevitEntityMap.load()에서 assertDefined() 호출

Forge-agents 내부 실행 흐름 (compiled, 로그에서 재구성):

  • CompareExtractor.compare()CompareExtractor.collectPrevEntities()RevitEntityMap.load()
  • RevitEntityMap.load()에서 이전 URN의 모든 model에 대한 derivative manifest를 로드
  • modelId 11의 derivative가 Forge API에서 반환되지 않음 → assertDefined 실패 (errorCode: 514)
  1. stderr 캡처: child-process.manager.ts:138-141 — assertion failure의 stack trace가 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}`);
});

기대 동작: 이전 revision에 포함된 모든 modelId에 대한 derivative가 Forge 플랫폼에 존재해야 한다. 실제 동작: modelId 11 ("EGI_OG1_18-11-2025_Issued_ACC")이 unpublished 상태로 derivative가 제거되었으나, 이전 revision의 entity query에 여전히 해당 modelId가 포함되어 있어 로드 시도 시 실패한다.

Log Evidence#

사용한 Datadog 쿼리:

text
service:cupixworks-any-bimrevision-agent status:error @environment:production
text
service:cupixworks-any-bimrevision-agent @environment:production "18046"

치명적 에러 로그 (bim_id 18046):

text
AssertionError [ForgeAgentError]: Must not be a nullable value: Derivative not found for modelId: 11 @ EntityMap.derivativesload()
  errorCode: 514
    at Object.assertDefined (.../cpassert.js:27:19)
    at RevitEntityMap.load (.../compare-extractor/entity-map.js:97:33)
    at async CompareExtractor.collectPrevEntities (.../compare-extractor/compare-extractor.js:991:13)
    at async extractComparison (.../app.js:133:20)
    at async Object.extract (.../app.js:82:25)
    at async BimCompareProcess.processMessage (/tmp/agent/dist/process/bim-compare.process.cjs:6435:16)

결과 에러 로그:

text
BimRevisionService::runBimCompare | Forge error: 514 - Must not be a nullable value: Derivative not found for modelId: 11 @ EntityMap.derivativesload()

비치명적 geometry assertion (bim_id 17157, 재시도 후 성공):

text
AssertionError: Assertion failed - Invalid OTG header
    at Object.assert (.../cpassert.js:30:19)
    at GeomDecoder.read (.../svf2-geometry-wss-loader.js:436:33)
    at SVF2GeometryWSSLoader.parseGeometryResource (.../svf2-geometry-wss-loader.js:258:37)
text
Retrying to parse geometry resource for hash 歧ꀎ㱍哻贜뿩焲湀, attempt 2 (waiting 10s)
Successfully parsed geometry resource for hash 歧ꀎ㱍哻贜뿩焲湀, attempt 2

작업 결과 비교:

bim_id facilityKey Error Type Outcome
17157 5k6vfs Invalid OTG header (geometry) 성공 (재시도 후)
18046 5k6vfs Derivative not found modelId:11 실패 (errorCode 514)

modelId 11 상태 (동일 facility의 다른 작업 로그에서 확인):

json
{
  "version": 2,
  "id": 11,
  "name": "EGI_OG1_18-11-2025_Issued_ACC",
  "published": false,
  "views": []
}

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Unpublished model의 derivative가 Forge에서 제거되었으나, 이전 revision의 entity query에 해당 modelId가 남아있어 assertDefined 실패 로그에서 "Derivative not found for modelId: 11" 확인. BIM_LEVEL_CHANGES 로그에서 modelId 11이 published: false이고 after 상태에서 제거됨. collectPrevEntities 단계에서 실패 (entity-map.js:97). errorCode 514. Confirmed
H2 Forge API 일시적 장애로 derivative 조회 실패 동일 시간대에 geometry decode 실패도 발생 (OTG header) geometry 건은 재시도로 성공. derivative 실패는 "not found"로 특정 modelId에 한정. 동일 facility의 다른 bim_id(17157, 19055, 18234, 17717)는 성공. Rejected
H3 Child process OOM 또는 리소스 부족으로 assertion 실패 Child process stderr에 에러 출력됨 에러 메시지가 명확히 "Derivative not found"를 지정. 다른 동시 작업은 정상 완료. 프로세스 killed signal 없음. Rejected

Fix Recommendation#

즉시 조치 (Critical)#

이 에러는 @cupixapps/forge-agents 외부 패키지 내부(entity-map.js:97)에서 발생하며, 서비스 자체 코드에서 직접 수정할 수 없다. 그러나 두 가지 접근이 가능하다:

  1. Entity query에서 unpublished model 필터링: bim-revision-service.tsrunBimCompare() 호출 전에, cpBim.getBimEntities() 결과에서 unpublished model에 속하는 entity를 제외하는 로직 추가. BIM_LEVEL_CHANGES 로그에서 확인된 것처럼 model의 published 상태를 확인할 수 있다면, entity query 구성 시 해당 modelId를 제외해야 한다.

  2. forge-agents 패키지 업데이트 요청: RevitEntityMap.load()에서 derivative 누락 시 assertDefined 대신 graceful skip + warning 로그를 남기도록 @cupixapps/forge-agents 팀에 개선 요청.

단기 개선 (1주 이내)#

  • bim-revision-service.tsrunBimCompare 파라미터 구성 시, 이전 revision의 forge model manifest를 사전 조회하여 derivative가 존재하지 않는 modelId를 entity query에서 제거하는 사전 검증 로직 추가.
  • ForgeErrorMapper에 errorCode 514를 명시적으로 매핑하여, "derivative not found" 에러의 의미를 API 상태 보고에 더 명확히 전달.

장기 개선 (재발 방지)#

  • @cupixapps/forge-agentsCompareExtractor.collectPrevEntities() 흐름에서 individual model derivative 로드 실패를 non-fatal로 처리하고, 해당 model의 entity를 건너뛰되 나머지 비교는 계속 진행하도록 설계 변경 (패키지 팀 협의 필요).
  • Facility에서 model이 unpublish될 때 관련 BIM revision의 entity reference를 정리하는 lifecycle hook 추가.

Monitoring#

  • 추가할 메트릭: forge.derivative.not_found 카운터 (modelId, facilityKey 태그 포함)
  • 알림 쿼리:
text
service:cupixworks-any-bimrevision-agent status:error "Derivative not found" @environment:production
text
service:cupixworks-any-bimrevision-agent "forgeErrorCode: 514" @environment:production

Risk Assessment#

  • Risk level: medium
  • 예상 복잡도: standard — 서비스 코드에서 entity query 필터링은 가능하나, 근본적 해결은 외부 패키지 변경 필요