ForgeAgent extract — out-of-bounds object ID reference in SVF2
RCA: Failed to traverse nodes: Failed to get properties and children
Overview#
What Happened#
2026-04-23 19:46 UTC에 cupixworks-any-room-agent 서비스에서 BIM ID 19176 (팀 benchmarkgc, 사용자 csollenberger@benchmarkgc.com)의 room extraction 처리 중 Autodesk Forge SVF2 property database 순회 과정에서 유효하지 않은 object ID 참조 에러가 발생했다. @cupix/forge-agents 라이브러리가 object ID 9217을 조회했으나, 해당 모델의 유효 범위가 1~9216이어서 property database 접근이 실패하고, 에러가 child process에서 parent process로 전파되며 최종적으로 SQS 메시지 삭제 및 BIM room_state를 Error로 전환하며 종료되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.message | Failed to traverse nodes: Failed to get properties and children |
| top_frame | @cupix/forge-agents (external library — property database traversal) |
| env | production, us-west-2 |
| BIM ID | 19176 |
| SQS Message ID | 23bb29ea-16f7-4adf-bbb2-8d3b38a34d42 |
| SQS Queue | cupix-tesla-room-agent-production |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| benchmarkgc (ID 1216) | 1 | BIM room extraction 실패 — room_state가 Error로 전환되어 해당 BIM 모델의 room 데이터 사용 불가 |
Timeline#
- 19:46:13.987Z — SQS 메시지 수신, BIM ID 19176 처리 시작 (
BaseService::runByMessage) - 19:46:14.028Z — Cupix 인증 세션 설정 (session_id:
01f25334a39bd45ae...) - 19:46:15.015Z —
RoomExtractManager::execute시작, Forge URN으로 SVF2 extraction 호출 - 19:46:18.200Z —
@cupix/forge-agents에서 root cause 에러 발생:Invalid object ID: 9217. Valid range is 1 to 9216. - 19:46:18.201Z — 에러 체인 전파 (get node → use property database → traverse nodes → ForgeAgents Error)
- 19:46:18.203Z —
BaseService::handlingMessageErrors에서 SQS 메시지 삭제 및 BIM Error state 업데이트 - 19:46:18.239Z — SQS 메시지 삭제 완료
Error Log#
Failed to traverse nodes: Failed to get properties and children
Impact#
- Service:
cupixworks-any-room-agent - 발생 횟수: 1
- 최초 발생: 2026-04-23T19:46:18.201Z
- 최근 발생: 2026-04-23T19:46:18.201Z
Root Cause Summary#
BIM ID 19176의 RVT 파일(19176_1776972884833_HLC_HB.rvt)을 Autodesk Forge SVF2 포맷으로 변환 후 room extraction을 수행하는 과정에서, @cupix/forge-agents 라이브러리가 property database를 순회할 때 object ID 9217을 참조했으나, 해당 모델의 유효 object ID 범위가 1~9216이었다. 이는 Forge 서버 측 model derivative 변환 결과에 경계 바깥의 object reference가 포함되었거나, RVT 원본 파일에 corrupt된 element가 존재하기 때문이다. ForgeAgent.extract()가 result.Error를 설정하여 반환했고, 이 에러 코드가 FORGE_SOURCE_INVALID_MANIFEST가 아니어서 SVF1 fallback이 트리거되지 않았고, "Failed to get rooms from svf2" 예외가 throw되었다. 에러는 child process → manager → service → BaseService로 전파되어 최종적으로 BIM의 room_state를 Error로 전환했다.
Technical Analysis#
Code Path#
- Entry point:
room-service.ts:58—run()메서드에서 BIM 처리 시작 room-service.ts:66-68— Forge 인증 후runRoomExtractor(cpBim)호출
if (cpBim.isGenerateMasterViews) {
await this.forgeAuth.authenticate();
await this.updateBimRoomState(targetId, TESLA.UpdateBimRequest.RoomStateEnum.Extracting);
await this.runRoomExtractor(cpBim);
room-service.ts:187—RoomExtractManager.execute()에 Forge API config, URN, format type 전달
const roomExtractorResults = await this.roomExtractManager.execute({
apiConfig: {
clientId: Environment.ADF_CLIENT_ID,
clientSecret: Environment.ADF_CLIENT_SECRET,
},
urn: cpBim.forgeUrn,
formatType: cpBim.forgeFormatType,
region: cpBim.forgeRegion
});
room-extract.manager.ts:29—ChildProcessManager.execute()로 child process에서 실행
try {
const result = await this.childProcessManager.execute<RoomExtractorResults>('execute', params);
logger.debug('RoomExtractManager::execute | completed successfully');
return result;
} catch (error) {
logger.error('RoomExtractManager::execute | error:', error);
const errorCode = ErrorCode.Agent.RoomExtractorExecute;
if (this.setJobErrorCode) {
this.setJobErrorCode(errorCode);
}
throw error;
}
- Failure point:
room-extractor.process.ts:40-52—ForgeAgent.extract()호출 후result.Error확인
if (params.formatType === 'svf2') {
const result = await ForgeAgent.extract(params.apiConfig, params.urn, {
extractRoom: true,
extractMeta: true,
region: params.region
});
if (result.Error == undefined) {
extract = result;
} else if (result.Error === ForgeAgentErrorCode.FORGE_SOURCE_INVALID_MANIFEST) {
this.log('RoomExtractorProcess::execute - svf2 manifest is invalid, try svf1 process');
extract = await RoomExtractor.room_extract(params.token, params.urn);
} else {
throw new Error('Failed to get rooms from svf2');
}
ForgeAgent.extract()는 내부적으로 property database를 순회하면서 object ID 9217을 참조했다. 해당 모델의 유효 범위(1~9216)를 벗어나서 "Invalid object ID" 에러가 발생했고, 이 에러는 result.Error에 에러 코드로 설정되어 반환되었다. 이 에러 코드가 FORGE_SOURCE_INVALID_MANIFEST와 다르기 때문에 SVF1 fallback 분기(line 48-50)로 진입하지 못하고, line 52에서 throw new Error('Failed to get rooms from svf2')가 실행되었다.
room-extractor.process.ts:66-75— catch 블록에서 에러 로깅 후 re-throw
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
const errorStack = error instanceof Error ? error.stack : undefined;
this.log(`RoomExtractorProcess::execute | error: ${errorMessage}`);
if (errorStack) {
this.log(`RoomExtractorProcess::execute | stack: ${errorStack}`);
}
throw error;
}
- Error propagation to BaseService:
base-service.ts:108-110에서 최종 catch
this._countWaitedToStopTask = 0;
try {
await this.runByMessages();
} catch (error) {
await this.handlingMessageErrors(error);
}
base-service.ts:290-311—handlingMessageErrors()에서 에러 분류 및 SQS 메시지 삭제
private getApiErrorToDeleteMessage = (error: any): any => {
if (error == undefined) {
logger.warn('BaseService::getApiErrorToDeleteMessage | undefined error');
return 'undefined error';
}
로그에서 "BaseService::getApiErrorToDeleteMessage | undefined error"가 출력된 것으로 보아, child process에서 parent process로 전파되는 과정에서 Error 객체가 직렬화에 실패하여 undefined로 수신된 것으로 추정된다. getApiErrorToDeleteMessage가 'undefined error' 문자열을 반환하면서 apiErrorObject != undefined 조건이 충족되어, SQS 메시지 삭제 및 updateErrorState() (room_state → Error)가 실행되었다.
Log Evidence#
Datadog에서 사용한 쿼리:
service:cupixworks-any-room-agent status:error @environment:production
service:cupixworks-any-room-agent @environment:production
에러 발생 전 정상 처리 로그 (info):
{"timestamp": "19:46:13.987Z", "level": "info", "message": "BaseService::runByMessage | id: 19176"}
{"timestamp": "19:46:14.028Z", "level": "info", "message": "CupixAuth::setSession | session_id: 01f25334a39bd45ae0a78c996f2b0c743fa71c06"}
{"timestamp": "19:46:15.015Z", "level": "info", "message": "RoomExtractManager::execute | urn: dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6dGVzbGEtcHJvZHVjdGlvbi8xOTE3Nl8xNzc2OTcyODg0ODMzX0hMQ19IQi5ydnQ="}
에러 체인 (error level, 19:46:18.200~18.201Z):
Failed to get properties and children: Invalid object ID: 9217. Valid range is 1 to 9216.
Failed to get node: Failed to get properties and children
Failed to use property database: Failed to get properties and children
Failed to traverse nodes: Failed to get properties and children
ForgeAgents - Error: { code: 1, message: Failed to get properties and children }
에러 후 처리 로그 (warn + info):
BaseService::getApiErrorToDeleteMessage | undefined error
{"timestamp": "19:46:18.203Z", "level": "info", "message": "AwsQueueManager::deleteMessage | begin - queue url: https://sqs.us-west-2.amazonaws.com/002596530511/cupix-tesla-room-agent-production"}
{"timestamp": "19:46:18.239Z", "level": "info", "message": "AwsQueueManager::deleteMessage | end - message id: 23bb29ea-16f7-4adf-bbb2-8d3b38a34d42"}
최종 에러 로그:
{"timestamp": "19:46:18.495Z", "level": "error", "message": "BaseService::handlingMessageErrors | Error and message object - {\"error\":\"undefined error\",\"sqsMessage\":{\"MessageId\":\"23bb29ea-16f7-4adf-bbb2-8d3b38a34d42\",\"Attributes\":{\"ApproximateReceiveCount\":\"1\"}}}"}
동일 시간대에 다른 호스트(ip-10-1-37-252)에서는 다른 BIM 모델들이 정상 처리되고 있었으며, 이 에러는 BIM ID 19176에 국한된 단건 발생이었다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | BIM RVT 파일에 corrupt된 element가 있어 Forge SVF2 변환 시 유효 범위 밖의 object ID가 생성됨 | 로그에 Invalid object ID: 9217. Valid range is 1 to 9216. — 정확히 1개 초과된 ID. Forge property database가 off-by-one 또는 corrupt element 참조 |
다른 BIM 모델들은 동일 시간대에 정상 처리됨 (서비스 자체 문제 아님) | Confirmed |
| H2 | Forge API 서버 측 일시적 오류로 property database가 불완전하게 반환됨 | object ID 범위 에러가 서버 측 데이터 문제일 수 있음 | 에러 메시지가 명확히 object ID 범위 검증 실패를 보여줌 — 일시적 오류라면 다른 유형의 에러(timeout, 500 등)가 나옴. 또한 단일 모델에서만 발생 | Rejected |
| H3 | @cupix/forge-agents 라이브러리의 SVF2 parser 버그로 잘못된 object ID를 생성 |
property database 순회 시 boundary check에서 실패한 점 | 에러 메시지 형태(Invalid object ID: 9217. Valid range is 1 to 9216)는 Forge SDK의 validation 로직에서 출력되는 것으로, 라이브러리가 아닌 데이터 문제를 가리킴. 다른 모델은 정상 |
Rejected |
| H4 | SVF1 fallback이 트리거되었어야 하나 에러 코드 매핑 누락으로 fallback 실패 | room-extractor.process.ts:48에서 FORGE_SOURCE_INVALID_MANIFEST만 fallback 조건으로 검사하며, property database 에러는 별도의 에러 코드를 가짐 |
SVF1 fallback은 manifest 자체가 invalid한 경우를 위한 것이며, property traversal 에러에 대해 SVF1도 동일한 결과를 낼 가능성이 높음 | Inconclusive |
Fix Recommendation#
즉시 조치 (Critical)#
- 특별한 코드 수정 없음. 이 에러는 특정 BIM 파일(19176)의 데이터 문제로 인한 단건 발생이며, 서비스는 정상적으로 에러를 처리하고 room_state를 Error로 전환했다.
- benchmarkgc 팀에게 해당 BIM 파일(
HLC_HB.rvt)의 재업로드 또는 Autodesk에서 파일 검증을 권장할 수 있다.
단기 개선 (1주 이내)#
room-extractor.process.ts:52에서 throw하는 에러 메시지를result.Error코드를 포함하도록 개선:throw new Error(\Failed to get rooms from svf2: ${result.Error}`)` — 현재 generic한 메시지로는 실제 Forge 에러 코드를 파악하기 어렵다.room-extractor.process.ts:48의 fallback 조건에 property database 관련 에러 코드를 추가하여, SVF2에서 실패 시 SVF1으로 재시도할지 검토. 단, SVF1에서도 동일한 결과가 나올 수 있으므로 실제 테스트 필요.
장기 개선 (재발 방지)#
@cupix/forge-agents라이브러리에서 property database traversal 시 invalid object ID를 skip하고 나머지 유효한 room/level 데이터를 반환하는 graceful degradation 지원 검토.- child process → parent process 간 Error 객체 직렬화 문제 개선. 현재 child process에서 throw한 Error가 parent에서
undefined로 수신되어,BaseService::getApiErrorToDeleteMessage에서"undefined error"로 기록됨. 에러 정보가 유실되면 디버깅이 어려워진다.
Monitoring#
- Forge property database 에러에 대한 별도 모니터링:
service:cupixworks-any-room-agent status:error "Invalid object ID"
- room extraction 에러율 추적 (BIM별):
service:cupixworks-any-room-agent status:error "Failed to get rooms from svf2"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial
- 단일 BIM 파일의 데이터 문제로 인한 단건 발생이며, 서비스 전체에 영향을 미치지 않는다. 동일 시간대에 다른 BIM 모델들은 정상 처리되었다.