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#
- 2026-04-24T06:01:50Z -- BIM id 19231 처리 시작 (
BaseService::runByMessage | id: 19231),RoomExtractManager::execute호출 - 2026-04-24T06:01:51Z --
@cupix/forge-agents라이브러리가 Autodesk APS API에서 OTG 변환 상태가 pending임을 감지, error code 205 반환 - 2026-04-24T06:01:51Z -- SQS 메시지 삭제 후 에러 처리 완료
- 2026-04-24T06:32:42Z -- BIM id 19232 처리 시작, 동일한 OTG pending 에러 발생
- 2026-04-24T06:32:44Z -- SQS 메시지 삭제 후 에러 처리 완료
Error Log#
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_state가 error로 설정됨. 해당 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)호출
- SQS 큐에서 메시지 수신 후
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 실행
- BIM 정보 로드 후
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 실행
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
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_state를 error로 설정. 재시도 없이 영구 실패로 처리된다.
Log Evidence#
Datadog 검색 쿼리:
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):
{
"timestamp": "2026-04-24T06:01:50Z",
"status": "info",
"message": "BaseService::runByMessage | id: 19231"
}
{
"timestamp": "2026-04-24T06:01:51Z",
"status": "info",
"message": "RoomExtractManager::execute | urn: dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLmpDUHdrOXhtU1ZDQV9zTkpKdWJsRFE_dmVyc2lvbj0x"
}
{
"timestamp": "2026-04-24T06:01:51Z",
"status": "error",
"message": "ForgeAgents - Error: { code: 205, message: OTG status is pending: {} }"
}
{
"timestamp": "2026-04-24T06:01:51Z",
"status": "warn",
"message": "BaseService::getApiErrorToDeleteMessage | undefined error"
}
{
"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):
{
"timestamp": "2026-04-24T06:32:42Z",
"status": "info",
"message": "BaseService::runByMessage | id: 19232"
}
{
"timestamp": "2026-04-24T06:32:43Z",
"status": "info",
"message": "RoomExtractManager::execute | urn: dXJuOmFkc2sud2lwcHJvZDpmcy5maWxlOnZmLm9fOGRXS0lsUVUyOUEwRFByanlHTWc_dmVyc2lvbj0x"
}
{
"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 분기에 진입한다.
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 에러 객체를 인식할 수 있도록 한다. 현재 plainError객체는 "undefined error"로 분류되어 에러 유형에 관계없이 메시지가 삭제된다.
장기 개선 (재발 방지)#
- BIM 모델의 Forge 변환 상태를 room extraction 시도 전에 확인하는 pre-check 로직 도입. Autodesk manifest API에서 OTG 변환 완료 여부를 먼저 확인하고, pending이면 처리를 연기한다.
ForgeAgentErrorCode에 대한 체계적인 에러 분류 체계를cupix-tesla-room-agent에도 도입 (bim-revision-agent의ForgeErrorMapper패턴 참고). Transient 에러(OTG pending, API rate limit)와 permanent 에러(geometry load fail, property error)를 구분하여 처리한다.
Monitoring#
추가할 메트릭/알림:
- Forge 에러 코드별 발생 빈도 모니터링
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을 수동으로 재트리거해야 한다.