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#
- 09:15:13Z — bim_id 17157 (facilityKey
5k6vfs) geometry decode 중 "Invalid OTG header" assertion 발생 (stderr로 출력) - 09:15:13Z — 동일 작업에서 "Failed to decode geometry buffer (raw size: 6220, size: 0)" assertion 발생
- 09:15:23Z — bim_id 17157 geometry 재시도 성공 (attempt 2)
- 09:17:30Z — bim_id 17157 성공적으로 완료 (2698 entities)
- 09:17:56Z — bim_id 18046 (facilityKey
5k6vfs) "Derivative not found for modelId: 11" assertion 2회 발생, 작업 실패
Error Log#
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#
- Entry point:
bim-revision-service.ts:603—runBimCompare()호출
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(), ... }
};
- Child process delegation:
bim-compare.manager.ts:53—BimCompareManager.execute()→ChildProcessManager.execute()
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;
}
}
- Child process execution:
bim-compare.process.ts:72—ForgeAgent.extract()호출
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
}
});
- Failure point (external package):
entity-map.js:97—RevitEntityMap.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)
- stderr 캡처:
child-process.manager.ts:138-141— assertion failure의 stack trace가 stderr로 출력되어 부모 프로세스에서 error 레벨로 로깅
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 쿼리:
service:cupixworks-any-bimrevision-agent status:error @environment:production
service:cupixworks-any-bimrevision-agent @environment:production "18046"
치명적 에러 로그 (bim_id 18046):
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)
결과 에러 로그:
BimRevisionService::runBimCompare | Forge error: 514 - Must not be a nullable value: Derivative not found for modelId: 11 @ EntityMap.derivativesload()
비치명적 geometry assertion (bim_id 17157, 재시도 후 성공):
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)
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의 다른 작업 로그에서 확인):
{
"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)에서 발생하며, 서비스 자체 코드에서 직접 수정할 수 없다. 그러나 두 가지 접근이 가능하다:
-
Entity query에서 unpublished model 필터링:
bim-revision-service.ts의runBimCompare()호출 전에,cpBim.getBimEntities()결과에서 unpublished model에 속하는 entity를 제외하는 로직 추가.BIM_LEVEL_CHANGES로그에서 확인된 것처럼 model의 published 상태를 확인할 수 있다면, entity query 구성 시 해당 modelId를 제외해야 한다. -
forge-agents 패키지 업데이트 요청:
RevitEntityMap.load()에서 derivative 누락 시assertDefined대신 graceful skip + warning 로그를 남기도록@cupixapps/forge-agents팀에 개선 요청.
단기 개선 (1주 이내)#
bim-revision-service.ts의runBimCompare파라미터 구성 시, 이전 revision의 forge model manifest를 사전 조회하여 derivative가 존재하지 않는 modelId를 entity query에서 제거하는 사전 검증 로직 추가.ForgeErrorMapper에 errorCode 514를 명시적으로 매핑하여, "derivative not found" 에러의 의미를 API 상태 보고에 더 명확히 전달.
장기 개선 (재발 방지)#
@cupixapps/forge-agents의CompareExtractor.collectPrevEntities()흐름에서 individual model derivative 로드 실패를 non-fatal로 처리하고, 해당 model의 entity를 건너뛰되 나머지 비교는 계속 진행하도록 설계 변경 (패키지 팀 협의 필요).- Facility에서 model이 unpublish될 때 관련 BIM revision의 entity reference를 정리하는 lifecycle hook 추가.
Monitoring#
- 추가할 메트릭:
forge.derivative.not_found카운터 (modelId, facilityKey 태그 포함) - 알림 쿼리:
service:cupixworks-any-bimrevision-agent status:error "Derivative not found" @environment:production
service:cupixworks-any-bimrevision-agent "forgeErrorCode: 514" @environment:production
Risk Assessment#
- Risk level: medium
- 예상 복잡도: standard — 서비스 코드에서 entity query 필터링은 가능하나, 근본적 해결은 외부 패키지 변경 필요