ES /docs

ForgeAgents - Error: { code: 205, message: OTG status is pending: {} }

RCA: ForgeAgents - Error: { code: 205, message: OTG status is pending: {} }

Overview#

What Happened#

2026-04-24 06:01~06:32 UTC 사이에 cupixworks-any-room-agent 서비스에서 Autodesk APS(구 Forge) OTG 변환이 아직 완료되지 않은 BIM 모델에 대해 room extraction을 시도하여 에러가 발생했다. 동일 시간대에 서로 다른 호스트 2대에서 총 2건 발생했으며, 각각 BIM id 19231, 19232에 대한 처리였다.

Quick Facts#

Field Value
exception.message ForgeAgents - Error: { code: 205, message: OTG status is pending: {} }
top_frame @cupix/forge-agents (internal npm package v10.16.0)
env production, us-west-2

Timeline#

  1. 2026-04-24T06:01:50Z -- BIM id 19231 처리 시작 (BaseService::runByMessage | id: 19231), RoomExtractManager::execute 호출
  2. 2026-04-24T06:01:51Z -- @cupix/forge-agents 라이브러리가 Autodesk APS API에서 OTG 변환 상태가 pending임을 감지, error code 205 반환
  3. 2026-04-24T06:01:51Z -- SQS 메시지 삭제 후 에러 처리 완료
  4. 2026-04-24T06:32:42Z -- BIM id 19232 처리 시작, 동일한 OTG pending 에러 발생
  5. 2026-04-24T06:32:44Z -- SQS 메시지 삭제 후 에러 처리 완료

Error Log#

Datadog Logs

text
ForgeAgents - Error: { code: 205, message: OTG status is pending: {} }

Impact#

  • Service: cupixworks-any-room-agent
  • 발생 횟수: 2 (Datadog 기준, 클러스터 파일은 1건 기록)
  • 최초 발생: 2026-04-24T06:01:51Z
  • 최근 발생: 2026-04-24T06:32:44Z

영향 받은 BIM 모델(id 19231, 19232)의 room extraction이 실패하여 room_stateerror로 설정됨. 해당 BIM에 대한 room/workarea 데이터가 생성되지 않았다. SQS 메시지가 삭제되었으므로 자동 재시도는 발생하지 않는다.

Root Cause Summary#

Autodesk APS(Forge)의 SVF2-to-OTG 변환이 아직 완료되지 않은 상태에서 room extraction을 시도한 것이 근본 원인이다. @cupix/forge-agents 라이브러리(v10.16.0)가 Autodesk APS manifest API를 호출하여 OTG 데이터를 가져오려 할 때, 변환 상태가 "pending"이면 error code 205를 반환한다. RoomExtractorProcess는 이 에러 코드를 FORGE_SOURCE_INVALID_MANIFEST와만 비교하고, 그 외에는 일괄적으로 throw new Error('Failed to get rooms from svf2')를 실행한다. OTG pending 상태는 일시적(transient) 조건이므로 재시도로 해결 가능하지만, 현재 코드에는 OTG pending에 대한 retry 로직이 없다.

Technical Analysis#

Code Path#

  • Entry point: BaseService::runByMessage (packages/base/src/base-service.ts:153)
    • SQS 큐에서 메시지 수신 후 run(targetId) 호출
packages/base/src/base-service.ts:153-191typescript
protected runByMessage = async (message: AWS.SQS.Message) => {
    this._messageInProcess = message;
    const messageBody = message.Body;
    if (messageBody && CPUtils.isJsonString(messageBody)) {
        const msgObject = JSON.parse(messageBody);
        const targetId = /* ... */ msgObject.id ?? msgObject.model?.id;
        // ...
        await this.run(targetId, msgObject);
        // ...
    }
};
  • RoomService::run (packages/cupix-tesla-room-agent/src/room-service.ts:58-100)
    • BIM 정보 로드 후 isGenerateMasterViews이면 Forge 인증 → room extraction 실행
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) {
        // ...
        if (cpBim.isGenerateMasterViews) {
            await this.forgeAuth.authenticate();
            await this.updateBimRoomState(targetId, TESLA.UpdateBimRequest.RoomStateEnum.Extracting);
            await this.runRoomExtractor(cpBim);
            // ...
        }
    }
};
  • RoomService::runRoomExtractor (packages/cupix-tesla-room-agent/src/room-service.ts:179-198)
    • RoomExtractManager.execute()를 호출하여 child process에서 room extraction 실행
packages/cupix-tesla-room-agent/src/room-service.ts:179-198typescript
private runRoomExtractor = async (cpBim: CPBim): Promise<void> => {
    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);
};
  • Failure point: RoomExtractorProcess::execute (packages/cupix-tesla-room-agent/src/process/room-extractor.process.ts:29-76)
    • ForgeAgent.extract() 호출 → result.Error가 정의되어 있고 FORGE_SOURCE_INVALID_MANIFEST가 아니면 일괄 throw
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');  // <-- OTG pending 포함 모든 에러가 여기로
    }
}

기대 동작: OTG 변환이 pending 상태일 때는 일정 시간 대기 후 재시도하거나, SQS visibility timeout을 활용하여 메시지를 다시 큐에 넣어야 한다.

실제 동작: OTG pending 에러(code 205)가 다른 에러와 동일하게 처리되어 즉시 throw → SQS 메시지 삭제 → BIM room_stateerror로 설정. 재시도 없이 영구 실패로 처리된다.

Log Evidence#

Datadog 검색 쿼리:

text
service:cupixworks-any-room-agent status:error "OTG status is pending"
Time range: 2026-04-24T05:30:00Z to 2026-04-24T07:30:00Z

첫 번째 발생 (BIM id 19231, 호스트 ip-10-1-164-202):

json
{
  "timestamp": "2026-04-24T06:01:50Z",
  "status": "info",
  "message": "BaseService::runByMessage | id: 19231"
}
json
{
  "timestamp": "2026-04-24T06:01:51Z",
  "status": "info",
  "message": "RoomExtractManager::execute | urn: dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLmpDUHdrOXhtU1ZDQV9zTkpKdWJsRFE_dmVyc2lvbj0x"
}
json
{
  "timestamp": "2026-04-24T06:01:51Z",
  "status": "error",
  "message": "ForgeAgents - Error: { code: 205, message: OTG status is pending: {} }"
}
json
{
  "timestamp": "2026-04-24T06:01:51Z",
  "status": "warn",
  "message": "BaseService::getApiErrorToDeleteMessage | undefined error"
}
json
{
  "timestamp": "2026-04-24T06:01:52Z",
  "status": "error",
  "message": "BaseService::handlingMessageErrors | Error and message object - {\"error\":\"undefined error\",\"sqsMessage\":{\"MessageId\":\"8cc44890-e3c1-4180-a05a-73dc7a4de6af\",\"Attributes\":{\"ApproximateReceiveCount\":\"1\"}}}"
}

두 번째 발생 (BIM id 19232, 호스트 ip-10-1-173-44):

json
{
  "timestamp": "2026-04-24T06:32:42Z",
  "status": "info",
  "message": "BaseService::runByMessage | id: 19232"
}
json
{
  "timestamp": "2026-04-24T06:32:43Z",
  "status": "info",
  "message": "RoomExtractManager::execute | urn: dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLm9fOGRXS0lsUVUyOUEwRFByanlHTWc_dmVyc2lvbj0x"
}
json
{
  "timestamp": "2026-04-24T06:32:44Z",
  "status": "error",
  "message": "ForgeAgents - Error: { code: 205, message: OTG status is pending: {} }"
}

에러 처리 과정에서 getApiErrorToDeleteMessage가 에러를 "undefined error"로 분류하는 이유: 에러 객체가 plain Error 인스턴스(throw new Error('Failed to get rooms from svf2'))로, errno/code/syscall 속성(Node.js system error)이 없고, response 속성(HTTP error)도 없으므로 line 249의 response = undefined 분기에 진입한다.

text
service:cupixworks-any-room-agent status:error
Time range: 2026-04-17T00:00:00Z to 2026-04-24T23:59:59Z

과거 7일간 동일 서비스의 ForgeAgents - Error 패턴: code 205(OTG pending) 외에도 code 305(geometry load fail), code 1(property database fail), code API_UNAUTHORIZED(401) 등 다양한 Forge 에러가 발생하며, 모두 동일한 "catch-all throw" 경로를 거침.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Autodesk APS OTG 변환이 아직 완료되지 않은 상태에서 room extraction 시도 에러 메시지 OTG status is pending: {}, code 205. 두 건 모두 동일 패턴. RoomExtractorProcess line 51에서 catch-all throw 경로 확인. -- Confirmed
H2 Forge API 인증 실패로 인한 에러 같은 날 다른 BIM에서 API_UNAUTHORIZED 에러 발생 사례 존재 (10:10:11 KST) OTG pending 건은 code 205이며 CupixAuth::setSession이 정상 완료된 로그 확인. 인증과 무관한 에러 코드. Rejected
H3 BIM 모델 자체가 손상되어 room extraction 불가 -- OTG pending은 변환 "진행 중" 상태를 의미하며, 모델 손상이 아닌 타이밍 문제. code 305(geometry fail)와 다른 에러 코드. Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 파일: packages/cupix-tesla-room-agent/src/process/room-extractor.process.ts:46-53
  • result.Error가 code 205(OTG pending)인 경우를 별도 분기로 처리하여, 즉시 throw 대신 재시도 가능한 에러로 분류해야 한다.
  • 접근 방식: OTG pending 에러(code 205)를 감지하면 에러를 throw하되, BaseService에서 해당 에러를 인식하여 SQS 메시지를 삭제하지 않도록 해야 한다. 이렇게 하면 SQS visibility timeout 후 메시지가 자동 재시도된다.

단기 개선 (1주 이내)#

  • RoomExtractorProcess에서 OTG pending 에러 발생 시 자체적으로 지수 백오프(exponential backoff) 재시도를 수행하는 로직을 추가한다. Autodesk OTG 변환은 보통 수 분 내에 완료되므로, 30초1분 간격으로 35회 재시도하면 대부분 해결된다.
  • getApiErrorToDeleteMessage의 에러 분류 로직을 개선하여, ForgeAgents 에러 객체를 인식할 수 있도록 한다. 현재 plain Error 객체는 "undefined error"로 분류되어 에러 유형에 관계없이 메시지가 삭제된다.

장기 개선 (재발 방지)#

  • BIM 모델의 Forge 변환 상태를 room extraction 시도 전에 확인하는 pre-check 로직 도입. Autodesk manifest API에서 OTG 변환 완료 여부를 먼저 확인하고, pending이면 처리를 연기한다.
  • ForgeAgentErrorCode에 대한 체계적인 에러 분류 체계를 cupix-tesla-room-agent에도 도입 (bim-revision-agentForgeErrorMapper 패턴 참고). Transient 에러(OTG pending, API rate limit)와 permanent 에러(geometry load fail, property error)를 구분하여 처리한다.

Monitoring#

추가할 메트릭/알림:

  • Forge 에러 코드별 발생 빈도 모니터링
text
service:cupixworks-any-room-agent "ForgeAgents - Error" status:error
  • OTG pending 에러 발생 후 동일 BIM id에 대한 재처리 성공 여부 추적
  • SQS 메시지 ApproximateReceiveCount가 높은 메시지 감지 (retry 루프 방지)

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: standard
  • 발생 빈도가 낮고(하루 2건) 사용자 영향이 특정 BIM 모델에 한정됨. 단, SQS 메시지가 삭제되어 자동 복구 불가하므로 해당 BIM의 room extraction을 수동으로 재트리거해야 한다.