ES /docs

@cupix/forge-agents SVF2 property DB boundary error

RCA: Failed to get node: Failed to get properties and children

Overview#

What Happened#

2026-04-23 19:46:18 UTC에 cupixworks-any-room-agent (실제 서비스명: cupix-tesla-room-agent) 서비스에서 BIM 모델(ID: 19176, 파일: HLC_HB.rvt)의 room extraction 처리 중 Autodesk Forge SVF2 property database 순회 과정에서 off-by-one 오류가 발생했다. @cupix/forge-agents 라이브러리가 property database의 유효 범위(1~9216)를 초과하는 object ID 9217에 접근을 시도하여 에러가 발생했으며, 동일 BIM 모델에서 2건의 반복 발생이 확인되었다.

Quick Facts#

Field Value
exception.message Failed to get node: Failed to get properties and children
top_frame @cupix/forge-agents (외부 패키지, 소스 비공개)
runtime Node.js (filebeat 7.17.15 수집)
env production, us-west-2

Timeline#

  1. 2026-04-14 00:53:16 KST — 동일 패턴의 최초 발생 (다른 모델, object ID: 4435162 vs max 4435158)
  2. 2026-04-14 21:58:23 KST — 두 번째 발생 (다른 모델, object ID: 1511932 vs max 1511930)
  3. 2026-04-22 21:26:12 KST — BIM ID 19176 (HLC_HB.rvt) 첫 번째 처리 시도 및 실패
  4. 2026-04-23 19:46:13 KST — BIM ID 19176 (HLC_HB.rvt) 두 번째 처리 시도 및 동일 에러로 실패
  5. 2026-04-24 — error-sweeper에 의해 클러스터 감지, RCA 수행

Error Log#

Datadog Logs

text
Failed to get node: Failed to get properties and children

Impact#

  • Service: cupixworks-any-room-agent
  • 발생 횟수: 1 (이 클러스터), 전체 동일 패턴 4건 (14일 이내)
  • 최초 발생: 2026-04-23T19:46:18.200Z
  • 최근 발생: 2026-04-23T19:46:18.200Z
  • 영향: BIM 모델 19176의 room extraction이 실패하며, BIM room state가 Error로 설정됨. 해당 모델의 room/workarea 데이터가 생성되지 않아 해당 facility의 room 기반 기능 사용이 불가. SQS 메시지는 삭제되어 자동 재시도되지 않음.

Root Cause Summary#

@cupix/forge-agents 라이브러리(v10.16.0)의 SVF2 property database 순회 로직에서 off-by-one boundary error가 발생한다. 라이브러리가 Autodesk Forge의 SVF2 모델 property database에서 node를 순회할 때, 최대 유효 object ID를 초과하는 ID에 접근을 시도한다. 이 에러는 @cupix/forge-agents 내부에서 발생하며 소스 코드가 비공개(pre-compiled npm 패키지)이므로, 라이브러리 수준에서의 수정이 필요하다. BIM ID 19176 (HLC_HB.rvt)에서 반복 발생(2건)하며, 다른 모델에서도 유사 패턴(object ID가 max 범위를 1~4 초과)이 확인되었다.

Technical Analysis#

Code Path#

  • Entry point: room-service.ts:58RoomService.run() 에서 BIM 모델 처리 시작
  • room-service.ts:68runRoomExtractor(cpBim) 호출
packages/cupix-tesla-room-agent/src/room-service.ts:58-68typescript
protected run = async (targetId: number): Promise<void> => {
    const cpBim = await this.createCPBimByBimId(targetId);
    if (cpBim) {
        this._modelInProcess = cpBim;
        this._forgeApi.updateRegionFromBim(cpBim.forgeRegion);
        if (cpBim.workspaceDirPath) this.setModelWorkspace(cpBim.workspaceDirPath);

        if (cpBim.isGenerateMasterViews) {
            await this.forgeAuth.authenticate();
            await this.updateBimRoomState(targetId, TESLA.UpdateBimRequest.RoomStateEnum.Extracting);
            await this.runRoomExtractor(cpBim);
  • room-service.ts:187RoomExtractManager.execute()로 child process 실행 위임
packages/cupix-tesla-room-agent/src/room-service.ts:179-198typescript
private runRoomExtractor = async (cpBim: CPBim): Promise<void> => {
    logger.debug('RoomService::runRoomExtractor | begin');
    if (cpBim.forgeUrn == undefined
        || cpBim.forgeFormatType == undefined
    ) {
        throw new Error('RoomService::runRoomExtractor | end - undefined params');
    }

    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
    });
    cpBim.fromRoomExtractor(roomExtractorResults);
    logger.debug('RoomService::runRoomExtractor | end');
};
  • room-extract.manager.ts:29ChildProcessManager.execute() 로 자식 프로세스에서 실행
packages/cupix-tesla-room-agent/src/manager/room-extract.manager.ts:25-43typescript
async execute(params: RoomExtractorParams): Promise<RoomExtractorResults> {
    logger.debug('RoomExtractManager::execute | begin with urn:', params.urn);

    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;
    }
}
  • room-extractor.process.ts:39-44 — SVF2 포맷 모델에 대해 ForgeAgent.extract() 호출
packages/cupix-tesla-room-agent/src/process/room-extractor.process.ts:39-53typescript
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');
    }
  • Failure point: @cupix/forge-agents 내부 — property database 순회 중 object ID 9217 접근 시도 (유효 범위 1~9216). 이 에러는 라이브러리 내부에서 throw되어 상위 catch로 전파됨.

  • base-service.ts:290-311 — 에러가 handlingMessageErrors에서 처리되며, getApiErrorToDeleteMessage에서 에러 객체의 response 속성이 없어 "undefined error"로 분류됨. SQS 메시지 삭제 후 BIM room state를 Error로 업데이트.

packages/base/src/base-service.ts:240-243typescript
private getApiErrorToDeleteMessage = (error: any): any => {
    if (error == undefined) {
        logger.warn('BaseService::getApiErrorToDeleteMessage | undefined error');
        return 'undefined error';

Log Evidence#

전체 에러 체인 (모두 동일 시각 2026-04-23T19:46:18.200Z):

text
service:cupixworks-any-room-agent status:error from:2026-04-23T19:40:00Z to:2026-04-23T19:50:00Z
text
[2026-04-24 04:46:13 KST] [info] BaseService::runByMessage | id: 19176
[2026-04-24 04:46:14 KST] [info] CupixAuth::setSession | session_id: 01f25334a39bd45ae0a78c996f2b0c743fa71c06
[2026-04-24 04:46:15 KST] [info] RoomExtractManager::execute | urn: dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6dGVzbGEtcHJvZHVjdGlvbi8xOTE3Nl8xNzc2OTcyODg0ODMzX0hMQ19IQi5ydnQ=
[2026-04-24 04:46:18 KST] [error] Failed to get properties and children: Invalid object ID: 9217. Valid range is 1 to 9216.
[2026-04-24 04:46:18 KST] [error] Failed to get node: Failed to get properties and children
[2026-04-24 04:46:18 KST] [error] Failed to use property database: Failed to get properties and children
[2026-04-24 04:46:18 KST] [error] Failed to traverse nodes: Failed to get properties and children
[2026-04-24 04:46:18 KST] [error] ForgeAgents - Error: { code: 1, message: Failed to get properties and children }
[2026-04-24 04:46:18 KST] [warn]  BaseService::getApiErrorToDeleteMessage | undefined error
[2026-04-24 04:46:18 KST] [info]  AwsQueueManager::deleteMessage | begin
[2026-04-24 04:46:18 KST] [info]  AwsQueueManager::deleteMessage | end
[2026-04-24 04:46:18 KST] [error] BaseService::handlingMessageErrors | Error and message object - {"error":"undefined error","sqsMessage":{"MessageId":"23bb29ea-16f7-4adf-bbb2-8d3b38a34d42","Attributes":{"ApproximateReceiveCount":"1"}}}

Forge URN 디코딩:

text
urn:adsk.objects:os.object:tesla-production/19176_1776972884833_HLC_HB.rvt

이전 발생 이력 (동일 패턴, 14일 이내):

text
service:cupixworks-any-room-agent status:error "Invalid object ID" from:2026-04-10T00:00:00Z to:2026-04-24T00:00:00Z
시각 Object ID Valid Range BIM ID
2026-04-14 00:53:16 KST 4,435,162 1 ~ 4,435,158 불명
2026-04-14 21:58:23 KST 1,511,932 1 ~ 1,511,930 불명
2026-04-22 21:26:17 KST 9,217 1 ~ 9,216 19176
2026-04-23 19:46:18 KST 9,217 1 ~ 9,216 19176

모든 케이스에서 접근 시도한 object ID가 유효 범위의 최대값을 1~4만큼 초과하는 패턴이 확인됨.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 @cupix/forge-agents의 SVF2 property database 순회 시 off-by-one boundary error 모든 에러에서 object ID가 max를 1~4 초과; 에러 메시지 "Invalid object ID: 9217. Valid range is 1 to 9216" 직접 확인; ForgeAgent.extract() 호출 직후 발생 Confirmed
H2 손상된 BIM 파일(HLC_HB.rvt)로 인해 Forge property database 자체가 비정상 BIM ID 19176에서 2회 반복 발생; 동일 object ID boundary (9216/9217) 다른 모델(4,435,158, 1,511,930)에서도 동일 패턴 발생 — 특정 파일 문제가 아닌 라이브러리 로직 문제 Rejected
H3 Forge API 서버 측 일시적 오류로 property database 응답이 불완전 동일 모델에서 동일 boundary(9216/9217)로 재현됨; Forge API 에러가 아닌 로컬 property database 순회 에러 Rejected
H4 SVF2 manifest가 invalid하여 잘못된 데이터를 반환 room-extractor.process.ts:48에 SVF2 manifest invalid 시 SVF1 fallback 로직 존재 에러가 manifest invalid가 아닌 property database 순회 단계에서 발생; ForgeAgentErrorCode.FORGE_SOURCE_INVALID_MANIFEST가 아닌 다른 에러 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • room-extractor.process.ts:39-53: ForgeAgent.extract() 호출 결과에서 property database 관련 에러를 별도로 처리하여, 해당 에러 발생 시 room extraction 결과를 빈 배열(Rooms: [], Levels: [])로 반환하는 graceful degradation 적용. 현재는 에러가 throw되어 전체 처리가 실패하고 BIM state가 Error로 설정되지만, property database 순회 실패는 room 정보만 누락될 뿐 BIM 처리 전체를 실패시킬 필요는 없음.

단기 개선 (1주 이내)#

  • @cupix/forge-agents 패키지를 업데이트하여 property database 순회 시 object ID boundary check를 수정. 패키지 관리팀에 "Invalid object ID" 에러에 대한 버그 리포트 및 패치 요청.
  • room-extract.manager.ts:32-41: 에러 로깅 시 에러 객체를 직접 전달하여 스택 트레이스를 보존. 현재 logger.error('...', error) 형태이나, getApiErrorToDeleteMessage에서 "undefined error"로 분류되어 디버깅 정보가 손실됨.

장기 개선 (재발 방지)#

  • SVF2 property database 순회 실패 시 SVF1 format으로 fallback하는 로직 추가. 현재 room-extractor.process.ts:48에서 FORGE_SOURCE_INVALID_MANIFEST 에러에 대해서만 SVF1 fallback이 적용되어 있으나, property database 에러에 대해서도 동일한 fallback 경로를 적용.
  • BaseService.getApiErrorToDeleteMessage()의 에러 분류 로직 개선: 현재 error.response가 없으면 "undefined error"로 분류하는데, Error 인스턴스의 message 속성을 활용하여 더 정확한 에러 분류 및 로깅 수행.

Monitoring#

  • Property database boundary 에러 모니터링:
text
service:cupixworks-any-room-agent status:error "Invalid object ID"
  • Room extraction 전체 실패율 모니터링:
text
service:cupixworks-any-room-agent status:error "ForgeAgents - Error"
  • BIM Error state 전환 빈도 추적을 위한 updateBimRoomState | state: error 로그 모니터링:
text
service:cupixworks-any-room-agent "updateBimRoomState" "Error"

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: standard
  • 근거: 발생 빈도가 낮고(14일간 4건), 특정 BIM 모델의 property database 구조에 의존하는 문제. room extraction 실패는 해당 facility의 room 기능에만 영향을 미치며, 다른 BIM 처리(forge translation 등)에는 영향 없음. 수정은 @cupix/forge-agents 패키지 업데이트 또는 에러 핸들링 개선으로 가능.