BaseService::handlingMessageErrors | sqsMessage - {"MessageId":"02d8aa9c-0173-422c-adaf-b6b0fc260ae9
RCA: BaseService::handlingMessageErrors | sqsMessage - {...}, error:
Overview#
What Happened#
2026-08-06 room-extraction agent(cupixworks-any-room-agent)에서 BaseService::handlingMessageErrors | sqsMessage - {...}, error: 로그가 error: 뒤가 공란인 채로 error 레벨로 기록되었다. 대표 메시지 자체로는 원인을 알 수 없으나, 같은 초의 인접 로그를 시간순 추적하면 실제 트리거는 자식 프로세스의 svf2 extraction failed: {"code":305,...} — 외부 Autodesk APS(Forge) SVF2 지오메트리 로드 실패다. 메시지는 정상 삭제되고 room_state 는 Error 로 전이되어 agent 는 설계대로 자가 처리했다. 즉 사용자/데이터 영향 없는 noise 이며, 대표 로그의 공란은 별개의 observability bug(로그 포맷 지정자-인자 불일치)다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Error (plain, message-only) |
| exception.message | Failed to get rooms from svf2 (child: svf2 extraction failed: {"code":305,...}) |
| top_frame | packages/base/src/base-service.ts:318 (log site) / cupix-tesla-room-agent/src/process/room-extractor.process.ts:52 (throw site) |
| runtime | Node.js (TypeScript agents monorepo, compiled dist/app.cjs), forge-agents 10.99.0 |
| deploy | develop build (app.cjs, base-service.ts:318 sqsMessage - %s, error: 포맷) |
| env | production, us-west-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| exyte (bim 17171) | 2 (cluster) | 사용자 영향 없음 — SVF2 실패 시 메시지 정상 삭제 + room_state=Error 전이 (self-heal) |
room-extraction 실패는 해당 BIM 의 room 데이터 미생성으로 이어지나, 에러 자체는 외부 APS 데이터 문제이며 agent 코드 결함이 아니다.
Timeline#
- 2026-08-06 22:54:14 KST —
BaseService::runByMessage | id: 17171— 메시지 수신, room extraction 시작 (RoomService::run | forge-agents version: 10.99.0) - 2026-08-06 22:54:48 KST — 자식 프로세스
[Child] RoomExtractorProcess::execute | svf2 extraction failed: {"code":305,"message":"Failed to load geometries for a3693b23-..."} - 2026-08-06 22:54:48 KST — 부모
Failed to get rooms from svf2throw →CupixAuth::handleError | Undefined response - 2026-08-06 22:54:48 KST —
AwsQueueManager::deleteMessage | end - message id: 02d8aa9c-...(메시지 삭제) +cleanUpAnythingRelatedModel | path: /tmp/workspace/17171 - 2026-08-06 22:54:48 KST —
BaseService::handlingMessageErrors | sqsMessage - {...}, error:(대표 로그, error 공란) - 2026-08-06 23:59:19 KST — 마지막 발생 (last_seen), 이후 재발 없음. status-board 인시던트
2026-08-06-svc-cupixworks-any-room-agent--unknown-1resolved.
Error Log#
BaseService::handlingMessageErrors | sqsMessage - {"MessageId":"02d8aa9c-0173-422c-adaf-b6b0fc260ae9","Attributes":{"ApproximateReceiveCount":"1"}}, error:
Impact#
- Service:
cupixworks-any-room-agent - Team: exyte
- 발생 횟수: 2
- 최초 발생: 2026-08-06 22:54:48 KST
- 최근 발생: 2026-08-06 23:59:19 KST
Root Cause Summary#
두 개의 별개 원인이 겹쳐 있다. (1) Underlying trigger = noise: room-extraction 자식 프로세스가 Autodesk APS(Forge) SVF2 모델 derivative 의 지오메트리를 로드하지 못해 {"code":305,...} 를 반환했고, room-extractor.process.ts:52 가 throw new Error('Failed to get rooms from svf2') 로 전파했다. 이는 외부 APS 데이터/변환 품질 문제이지 agent 코드 결함이 아니며, BaseService 는 메시지를 정상 삭제하고 room_state 를 Error 로 전이시켜 자가 처리한다(ApproximateReceiveCount:"1" — 첫 수신에 삭제, 재시도 루프 아님). (2) Observability bug: 대표 로그의 error: 공란은 base-service.ts:318 로그 포맷 문자열이 %s 지정자를 1개만 두고 splat 인자를 2개(JSON.stringify(sqsMessage), errorAndMessage.error) 넘기기 때문이다. 두 번째 인자(error)는 winston splat 이 numeric key 로 저장하고, cplogger 의 filterNumericKeys 가 출력 직전 numeric key 를 전부 제거하여 error 내용이 통째로 유실된다. 따라서 원인 진단이 대표 로그만으로는 불가능해지는 진단성 결함이다.
Technical Analysis#
Code Path#
Entry: packages/base/src/base-service.ts:108 (runByMessages) → 실패 시 :110 handlingMessageErrors(error).
svf2 실패 발생 지점 (room-agent 자식 프로세스):
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 가 반환한 result.Error 가 undefined 도 FORGE_SOURCE_INVALID_MANIFEST 도 아닌 경우(=code 305 등) Failed to get rooms from svf2 를 throw 한다. 실제 code 305 문자열(svf2 extraction failed: {...})은 외부 @cupix/forge-agents 패키지(10.99.0)가 만들며 in-repo 소스에 없다(deployed build).
에러 처리 및 삭제 판정:
const response = CPUtils.isJsonString(error) ? JSON.parse(error) : error.response;
if (response == undefined) {
logger.warn('BaseService::getApiErrorToDeleteMessage | undefined response', error);
return 'undefined response';
}
plain Error 는 .response 가 undefined → getApiErrorToDeleteMessage 가 문자열 'undefined response' 를 반환한다(undefined 아님). 이 문자열은 truthy 이므로 삭제 분기가 실행된다:
const apiErrorObject = this.getApiErrorToDeleteMessage(error);
if (apiErrorObject != undefined || this.checkReceiveCountToDeleteMessage()) {
try {
errorAndMessage.error = apiErrorObject;
await this.deleteByMessage(this.messageInProcess);
if (this._modelInProcess != undefined && this._modelInProcess.id > 0) await this.updateErrorState(this._modelInProcess);
} catch (error) {
logger.error('BaseService::handlingMessageErrors | Errors in error handling', error);
}
}
logger.error('BaseService::handlingMessageErrors | Error and message object - %s', JSON.stringify(errorAndMessage));
주의: 위 스니펫은 현재 working tree(:311, Error and message object - %s — 단일 인자, 유실 없음)다. 그러나 이 클러스터의 대표 로그는 origin/develop HEAD 에 배포된 포맷이며, 그 형태는 아래와 같이 %s 1개 + splat 2개다:
logger.error('BaseService::handlingMessageErrors | sqsMessage - %s, error:',
JSON.stringify(errorAndMessage.sqsMessage), errorAndMessage.error);
포맷 문자열에는 %s 지정자가 1개뿐이라 첫 번째 splat(JSON.stringify(sqsMessage))만 소비된다. 두 번째 splat(errorAndMessage.error)은 winston 이 splat[1] numeric key 로 저장하고, cplogger 가 출력 직전 제거한다:
export const filterNumericKeys = (obj: any): any => {
const filtered: any = {};
Object.keys(obj).forEach(key => {
if (!/^\d+$/.test(key)) {
filtered[key] = obj[key];
}
});
return filtered;
};
기대 동작: 로그에 error 내용(message/stack)이 남아야 함. 실제 동작: error: 뒤가 공란, raw attribute error 필드 부재 → 원인 진단 불가.
Failure point: packages/base/src/base-service.ts:318-319 (splat 인자 유실) / underlying = 외부 APS SVF2 305.
Log Evidence#
svf2 트리거 확인 쿼리:
service:cupixworks-any-room-agent "svf2 extraction failed"
14일 창 3건, 매번 다른 objectGUID(산발적, 특정 모델 재진입 아님):
2026-08-06 22:54:48 error [Child] RoomExtractorProcess::execute | svf2 extraction failed: {"code":305,"message":"Failed to load geometries for a3693b23-78ce-fe02-689f-a251be5eba24"}
2026-08-06 10:01:13 error [Child] RoomExtractorProcess::execute | svf2 extraction failed: {"code":305,"message":"Failed to load geometries for 99d37c96-d1f6-e2c4-b000-0014b473df40"}
2026-07-31 16:56:49 error [Child] RoomExtractorProcess::execute | svf2 extraction failed: {"code":305,"message":"Failed to load geometries for 3475fe2c-44d7-d6ed-599c-c2d4b65c3f87"}
대표 메시지의 MessageId(02d8aa9c)로 self-heal 확인 쿼리:
service:cupixworks-any-room-agent "02d8aa9c"
같은 초에 handlingMessageErrors(error 공란)와 deleteMessage | end(삭제 완료)가 함께 기록됨:
2026-08-06 22:54:48 error BaseService::handlingMessageErrors | sqsMessage - {"MessageId":"02d8aa9c-0173-422c-adaf-b6b0fc260ae9","Attributes":{"ApproximateReceiveCount":"1"}}, error:
2026-08-06 22:54:48 info AwsQueueManager::deleteMessage | end - message id: 02d8aa9c-0173-422c-adaf-b6b0fc260ae9
bim 17171 전체 타임라인 (service:cupixworks-any-room-agent @bim.id:17171):
22:54:14 info BaseService::runByMessage | id: 17171
22:54:14 info RoomService::run | forge-agents version: 10.99.0, forge-utils version: 10.99.0
22:54:48 error [Child] RoomExtractorProcess::execute | svf2 extraction failed: {"code":305,"message":"Failed to load geometries for a3693b23-..."}
22:54:48 error Failed to get rooms from svf2
22:54:48 warn CupixAuth::handleError | Undefined response: {"stack":"Error: Failed to get rooms from svf2\n at ChildProcessManager2.handleMessage (/tmp/agent/dist/app.cjs:5429:26)...","message":"Failed to get rooms from svf2"}
22:54:48 info AwsQueueManager::deleteMessage | begin - queue url: https://sqs.us-west-2.amazonaws.com/002596530511/cupix-tesla-room-agent-production
22:54:48 info AwsQueueManager::deleteMessage | end - message id: 02d8aa9c-0173-422c-adaf-b6b0fc260ae9
22:54:48 info BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/17171
22:54:48 error BaseService::handlingMessageErrors | sqsMessage - {...02d8aa9c...ApproximateReceiveCount:"1"}, error:
ApproximateReceiveCount:"1" = 첫 수신에 삭제 완료 → 재시도 루프 없음. stack 의 /tmp/agent/dist/app.cjs 는 컴파일된 배포 빌드임을 확증(소스 트리 아님).
배포 포맷 검증 (git):
git show origin/develop:.../base-service.ts → :318 'sqsMessage - %s, error:' (splat 2개)
git merge-base --is-ancestor f9860dc41 origin/develop → YES
git merge-base --is-ancestor f9860dc41 origin/master → YES
TSLA-13277 커밋 f9860dc41("pass error to logger as splat instead of JSON.stringify")이 splat 포맷을 도입했고 현재 develop/master 양쪽의 조상이다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | 대표 로그의 실제 트리거 = 외부 APS SVF2 code:305 지오메트리 로드 실패 (noise) | bim 17171 타임라인상 같은 초 svf2 extraction failed: {"code":305,...} → Failed to get rooms from svf2 → deleteMessage; 14d 3건 산발 distinct objectGUID |
— | Confirmed |
| H2 | error: 공란은 로그 포맷 지정자-인자 불일치(observability bug) |
base-service.ts:318 %s 1개 + splat 2개; cplogger filterNumericKeys 가 numeric key 제거 (cplogger.ts:45-53); raw attribute error 부재 |
— | Confirmed |
| H3 | 메시지가 삭제되지 않고 10회 재시도된다 | — | ApproximateReceiveCount:"1" + 같은 초 deleteMessage / end; getApiErrorToDeleteMessage 가 'undefined response'(truthy) 반환 → 삭제 분기 즉시 실행 (base-service.ts:250-253,301) |
Rejected |
| H4 | agent 코드 결함으로 인한 500/데이터 손상 | — | throw 후 정상 삭제 + room_state=Error 전이 + cleanUpAnythingRelatedModel; SVF2 실패 원인은 외부 forge-agents 반환값 |
Rejected |
| H5 | 외부 의존성 대규모 outage (dep:* 인시던트) | status-board scope 는 svc:cupixworks-any-room-agent::unknown (svc:, dep: 아님), sibling e20ceddf 와 그룹핑된 resolved 인시던트일 뿐 |
3건/14d 저빈도, distinct objectGUID | Rejected |
Fix Recommendation#
이 클러스터는 noise 이며 코드 수정 대상이 아니다(underlying trigger = 외부 APS 데이터 품질). 권장사항은 진단성/관측성 개선에 한정한다.
즉시 조치 (Critical)#
- 없음. ET IGNORE 권장. 에러는 설계대로 자가 처리되며 사용자 영향 0.
단기 개선 (1주 이내)#
packages/base/src/base-service.ts:318-319(origin/develop): 포맷 지정자와 splat 인자 개수를 일치시켜 error 유실 제거. 예: 포맷 문자열에 error 를 명시적으로 반영하거나(%s추가) error 를 사전 직렬화(error instanceof Error ? error.stack ?? error.message : String(error))하여 numeric key 로 밀려나지 않게 한다.filterNumericKeys(cplogger.ts:45-53)가 진단 정보를 삼키는 부작용도 함께 재검토.base-service.ts:303에서apiErrorObject가 실제 API 에러 객체가 아닌'undefined response'문자열일 때 원본 error 의 message/stack 을 보존하도록 개선(현재는 원본 예외가 소실됨).room-extractor.process.ts:52throw 메시지에 code 포함:Failed to get rooms from svf2 (code: ${result.Error})— 로그만으로 305 식별 가능하게.
장기 개선 (재발 방지)#
- 로그에
DD_VERSION(deploy 버전) 주입 (cplogger.ts로그 메타) — 현재 version 태그 부재로 develop/master 혼재 배포 판별이 git 로만 가능. - IPC 경계(
packages/base/src/manager/child-process.manager.tsreject site)에서 자식 프로세스 에러의 구조화 payload(.code/.response)를 message-only Error 로 축소하지 않고 보존. - 외부 APS(Forge) SVF2 변환 품질 조사: code 305("Failed to load geometries")가 지속되면 재변환 트리거 또는 svf1 fallback 확대 검토. SVF2 실패 로그는 5xx 서버 에러와 구분해 warn 강등 고려.
Monitoring#
svf2 305 트리거 발생 추이 (release dashboard timeseries widget):
service:cupixworks-any-room-agent "svf2 extraction failed"
handlingMessageErrors 전체 error 볼륨 (observability bug 포맷 포함 추이):
service:cupixworks-any-room-agent status:error "handlingMessageErrors"
svf2 실패 후 self-heal(메시지 삭제) 정상 동작 확인:
service:cupixworks-any-room-agent "deleteMessage" "end"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (noise; 코드 수정 시 로그 포맷 1~2줄 조정에 한정)