Cannot read properties of undefined (reading 'Levels')
RCA: Cannot read properties of undefined (reading 'Levels')
Overview#
What Happened#
2026-06-29 23:12 KST, cupixworks-any-room-agent (Cupix Tesla Room Agent) 가 BIM id 20418 메시지를 처리하던 중 자식 프로세스(RoomExtractorProcess)에서 Cannot read properties of undefined (reading 'Levels') TypeError 가 발생했다. SQS 메시지는 정상적으로 삭제되었지만 BIM 의 room/level 추출이 실패하여 BaseService::handlingMessageErrors로 "undefined response" 가 기록되었다. 단발성 1건이며 같은 큐의 직전(20417) / 직후(20419, 20420) BIM 은 모두 정상 처리되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | TypeError |
| exception.message | Cannot read properties of undefined (reading 'Levels') |
| top_frame | /tmp/agent/dist/app.cjs:5367:26 (bundled ChildProcessManager.handleMessage rethrow site) |
| origin | packages/cupix-tesla-room-agent/src/process/room-extractor.process.ts:58-62 (child process) |
| env | production, us-west-2 |
| affected bim_id | 20418 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| gilbaneco (tenant: cupix) | 1 | 1 BIM (id 20418) 의 room/level extraction 실패. BIM 의 room_state 가 Extracted 로 업데이트되지 못함 (코드 흐름이 runRoomExtractor 에서 throw 되어 후속 updateBimRoomState(...Extracted) 가 실행되지 않음). |
Timeline#
- 2026-06-29 23:12:26 KST —
BaseService::runByMessage | id: 20418— BIM 20418 처리 시작 (RoomService::run). - 2026-06-29 23:12:26 KST —
CupixAuth::setSession | session_id: be789844db7da3a2eb3c1b42f86116929d1ee5fe— 세션 설정 완료, 이후roomExtractManager.execute호출. - 2026-06-29 23:12:29 KST — 자식 프로세스에서
extract가 undefined 인 상태로extract.Levels접근 → TypeError. 부모 프로세스ChildProcessManager.handleMessage가pending.reject(new Error(response.error))로 rethrow. - 2026-06-29 23:12:29 KST —
CupixAuth::handleError | Undefined response: { stack: "Error: Cannot read properties of undefined (reading 'Levels')..." }— axiosresponse가 없는 일반 Error 로 warn 로깅. - 2026-06-29 23:12:29 KST —
BaseService::handlingMessageErrors | Error and message object - {"error":"undefined response","sqsMessage":{"MessageId":"005426f3-..."}}— error 로깅 및 SQS 메시지 삭제 처리. - 2026-06-29 23:30:12 KST — 다음 메시지 (BIM 20419) 정상 처리 (
bim level size: 50, room size: 0).
Error Log#
Cannot read properties of undefined (reading 'Levels')
Datadog 원본 stack trace:
Error: Cannot read properties of undefined (reading 'Levels')
at ChildProcessManager2.handleMessage (/tmp/agent/dist/app.cjs:5367:26)
at ChildProcess.<anonymous> (/tmp/agent/dist/app.cjs:5295:16)
at ChildProcess.emit (node:events:524:28)
at ChildProcess.emit (node:domain:489:12)
at emit (node:internal/child_process:950:14)
at process.processTicksAndRejections (node:internal/process/task_queues:83:21)
Impact#
- Service:
cupixworks-any-room-agent(repo:cupixworks, package:cupix-tesla-room-agent) - Team: gilbaneco
- 발생 횟수: 1
- 최초 발생: 2026-06-29 23:12 KST
- 최근 발생: 2026-06-29 23:12 KST
영향 범위는 단일 BIM (id 20418) 으로 제한된다. 동일 큐의 인접 메시지(20417, 20419, 20420)는 정상 처리되었으므로 인프라/배포 수준의 회귀가 아니라 특정 BIM input 데이터에 한정된 결함으로 판단된다.
Root Cause Summary#
RoomExtractorProcess.execute 가 ADF/Forge 모델에서 room/level 정보를 추출할 때, 외부 라이브러리(RoomExtractor.room_extract — svf1 path, 또는 ForgeAgent.extract — svf2 path) 호출 결과를 받아 extract.Levels 를 그대로 읽는데, BIM 20418 입력에 대해 라이브러리가 undefined 를 반환(또는 throw 후 swallow)했고 그 결과 extract 변수가 undefined 상태에서 extract.Levels 접근이 TypeError 를 일으켰다. 자식 프로세스의 try/catch 가 에러를 sendError 로 부모에게 전달했고, 부모 ChildProcessManager.handleMessage 가 new Error(response.error) 로 재생성하여 stack 이 부모 라인을 가리키게 되었다. 정상 경로에서 동작한 다른 BIM 들과 달리 이 특정 BIM 의 Forge URN/format 에 대해 라이브러리가 빈/잘못된 응답을 돌려준 것이 직접 원인이다.
Technical Analysis#
Code Path#
- Entry point:
applications/agents/packages/cupix-tesla-room-agent/src/room-service.ts:58(RoomService.run) runRoomExtractor→RoomExtractManager.execute가 자식 프로세스에execute메시지 전송- 자식 프로세스 처리:
applications/agents/packages/cupix-tesla-room-agent/src/process/room-extractor.process.ts:29-65 - Failure point:
applications/agents/packages/cupix-tesla-room-agent/src/process/room-extractor.process.ts:58-62—extract가 undefined 인 상태에서extract.Levels접근 - Rethrow site (stack trace 가 가리키는 곳):
applications/agents/packages/base/src/manager/child-process.manager.ts:182—pending.reject(new Error(response.error || 'Unknown error'))
자식 프로세스의 실패 지점:
private async execute(params: any): Promise<any> {
try {
this.log('RoomExtractorProcess::execute | start');
let extract: {
Rooms?: ForgeAgent.IRoom[] | RoomExtractor.Room[];
Levels?: ForgeAgent.IRoomLevel[] | RoomExtractor.Level[];
Meta?: ForgeAgent.IMeta;
};
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');
}
} else {
extract = await RoomExtractor.room_extract(params.token, params.urn);
}
const extractorResults = {
Levels: extract.Levels, // ← extract 가 undefined 면 여기서 TypeError
Rooms: extract.Rooms,
Meta: extract.Meta
};
this.log('RoomExtractorProcess::execute | Room extraction completed successfully');
return extractorResults;
} catch (error) {
...
핵심: 라인 50 / 55 의 RoomExtractor.room_extract(params.token, params.urn) 호출에서 params.token 은 manager 가 보낸 params 에 존재하지 않는 필드이다(RoomExtractorParams 인터페이스는 apiConfig, urn, formatType, region 만 정의):
export interface RoomExtractorParams {
apiConfig: {
clientId: string;
clientSecret: string;
}
urn: string;
formatType: string;
region: string;
}
따라서 svf1 fallback (또는 formatType !== 'svf2') 경로로 진입하면 params.token === undefined 인 상태로 room_extract 가 호출된다. 라이브러리가 token undefined 인 경우 (a) undefined 를 반환하거나 (b) 빈 에러로 swallow 한 결과 extract 가 undefined 상태가 되면 라인 58 extract.Levels 가 TypeError 를 던진다. (uncertain — 라이브러리 내부 구현은 본 RCA 범위 밖이므로 "라이브러리가 undefined 를 반환"하는 분기는 단정할 수 없음. 그러나 결과 extract 가 undefined 가 된 사실은 stack trace 의 reading 'Levels' 메시지로부터 확정적.)
부모 측에서 stack 이 rethrow 되는 위치:
private handleMessage(response: MessageResponse): void {
if (response.type === 'ready') { this.emit('ready'); return; }
if (response.type === 'log') { logger.debug(`[Child] ${response.data}`); return; }
if (response.type === 'log_error') { logger.error(`[Child] ${response.data}`); return; }
const pending = this.pendingMessages.get(response.id);
if (!pending) {
logger.warn(`ChildProcessManager::handleMessage | No pending message for ID: ${response.id}`);
return;
}
this.pendingMessages.delete(response.id);
if (pending.timer) clearTimeout(pending.timer);
if (response.success) {
pending.resolve(response.data);
} else {
pending.reject(new Error(response.error || 'Unknown error')); // ← stack trace 가 가리키는 라인
}
}
자식이 sendError(message.id, error) 로 보낸 메시지를 부모가 받아 new Error(...) 로 다시 던진다. 따라서 Datadog 에 찍힌 stack trace 의 top frame 은 원래 발생 지점이 아니라 IPC 경계의 재생성 지점이다. 원인 분석에는 자식 측 코드(room-extractor.process.ts)를 봐야 한다.
Log Evidence#
Datadog 쿼리 (재현용):
service:cupixworks-any-room-agent "Levels"
핵심 로그 시퀀스 (시간 오름차순, KST):
2026-06-29 23:12:26 info BaseService::runByMessage | id: 20418
2026-06-29 23:12:26 info CupixAuth::setSession | session_id: be789844db7da3a2eb3c1b42f86116929d1ee5fe
2026-06-29 23:12:26 info RoomService::run | forge-agents version: 10.99.0, forge-utils version: 10.99.0
2026-06-29 23:12:29 error Cannot read properties of undefined (reading 'Levels')
2026-06-29 23:12:29 warn Cannot read properties of undefined (reading 'Levels')
2026-06-29 23:12:29 warn CupixAuth::handleError | Undefined response: {"stack":"Error: Cannot read properties of undefined (reading 'Levels')\n at ChildProcessManager2.handleMessage (/tmp/agent/dist/app.cjs:5367:26)\n at ChildProcess.<anonymous> (/tmp/agent/dist/app.cjs:5295:16)\n at ChildProcess.emit (node:events:524:28)\n at ChildProcess.emit (node:domain:489:12)\n at emit (node:internal/child_process:950:14)\n at process.processTicksAndRejections (node:internal/process/task_queues:83:21)","message":"Cannot read properties of undefined (reading 'Levels')"}
2026-06-29 23:12:29 info AwsQueueManager::deleteMessage | begin - queue url: https://sqs.us-west-2.amazonaws.com/002596530511/cupix-tesla-room-agent-production
2026-06-29 23:12:29 info BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/20418
2026-06-29 23:12:29 error BaseService::handlingMessageErrors | Error and message object - {"error":"undefined response","sqsMessage":{"MessageId":"005426f3-53b8-40be-bd88-35d51c2510f8","Attributes":{"ApproximateReceiveCount":"1"}}}
2026-06-29 23:12:29 info AwsQueueManager::deleteMessage | end - message id: 005426f3-53b8-40be-bd88-35d51c2510f8
전후 메시지 비교 — 정상 처리된 인접 BIM 들은 CPBim::fromRoomExtractor | bim level size: N, room size: 0 가 정상 출력됨:
2026-06-29 23:03:33 info BaseService::runByMessage | id: 20417
2026-06-29 23:03:58 info CPBim::fromRoomExtractor | bim level size: 5, room size: 0
2026-06-29 23:30:12 info BaseService::runByMessage | id: 20419
2026-06-29 23:30:58 info CPBim::fromRoomExtractor | bim level size: 50, room size: 0
2026-06-29 23:32:03 info BaseService::runByMessage | id: 20420
2026-06-29 23:32:21 info CPBim::fromRoomExtractor | bim level size: 55, room size: 0
20418 에 대해서만 CPBim::fromRoomExtractor 로그가 누락 — 즉 runRoomExtractor 가 cpBim 의 결과를 채우기 전에 throw 되었음. 부분 처리 (updateBimRoomState(...Extracting) 이후 Extracted 미도달) 가 발생했음을 의미한다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | 자식 프로세스의 room-extractor.process.ts:58 에서 extract 가 undefined 인 상태로 extract.Levels 가 호출되어 TypeError 가 발생했고, 부모 ChildProcessManager.handleMessage 가 IPC 경계에서 rethrow 한 것 |
stack message 가 정확히 'Levels' 를 가리킴; bundled stack top 이 부모 handleMessage (rethrow site, child-process.manager.ts:182); 자식의 try/catch 가 모든 에러를 sendError 로 위임; 20418 만 CPBim::fromRoomExtractor 로그가 누락 (extractor 결과를 받지 못함) |
— | Confirmed |
| H2 | cpbim.ts:78 의 this.roomExtractorResults?.Levels.length 가 Levels 가 없는 결과 객체를 받아 throw |
같은 키 이름 Levels 가 등장; 결과 형태가 비표준일 가능성 |
Optional chain ?. 는 roomExtractorResults 가 undefined 면 short-circuit; 객체가 있고 .Levels 가 없으면 .length 에서 throw 되어 메시지가 'length' 가 되어야 함. 또한 cpbim 의 fromRoomExtractor 가 호출되었다면 그 직전 `RoomExtractorProcess::execute |
Room extraction completed successfully` 자식 log 가 나와야 하나 부재 |
| H3 | 외부 의존성 outage (Forge / Autodesk API) | 동시간대 다수 클러스터가 폭증해야 함 | status-board: dep:* active 없음, 동일 서비스 인접 메시지 정상 처리, 단발성 1건 |
Rejected |
| H4 | 배포 회귀 (forge-agents 10.99.0 신규 도입) | 버전 정보 로그 존재 | 동일 버전으로 20417/20419/20420 모두 정상 처리 | Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
applications/agents/packages/cupix-tesla-room-agent/src/process/room-extractor.process.ts:46-65에서extract가 undefined 인 케이스를 명시적으로 처리한다. 두 가지 보강이 필요:result자체에 대한 nil guard (svf2 path 라인 46 의result.Error == undefined직전에result == undefined분기 처리)- svf1 path (
RoomExtractor.room_extract호출) 후extract == undefined체크 추가 후 의미 있는 에러 throw — 예:throw new Error(\RoomExtractor.room_extract returned undefined for urn=${params.urn}, formatType=${params.formatType}`)`
- 동시에
RoomExtractor.room_extract(params.token, params.urn)호출이 사용하는params.token은RoomExtractorParams인터페이스에 존재하지 않는 필드다 (room-extract.manager.ts:58-66). svf1 fallback 자체가 항상 undefined token 으로 호출되고 있을 가능성이 높다 — manager 의 params 정의 또는 process 의 호출 시그니처 중 어느 쪽이 옳은지 확인하고 정합성을 맞춰야 한다. (uncertain — 라이브러리가 token 을 옵션으로 허용한다면 영향 없을 수 있음; 확인 필요.)
단기 개선 (1주 이내)#
room-extractor.process.ts:66-77의 catch 가 자식 측 stack 을 그대로sendError에 넘기지 못해(파라미터가Error.message만 사용됨,base.process.ts:56-59) 부모 측에서 원본 stack 이 유실된다.BaseProcess.sendError가 error.stack 까지 전송하도록 확장하고,ChildProcessManager.handleMessage(child-process.manager.ts:182) 가 받은 stack 을 새 Error 의 stack 에 부착하면 향후 동일 사고에서 IPC 경계 너머의 원인 라인이 Datadog 에 그대로 노출된다.RoomService.run(room-service.ts:58) 에서runRoomExtractor실패 시 BIM 의room_state가Extracting상태로 멈춘 채 SQS 가 삭제되는 흐름을 점검.updateErrorState(line 512-517) 가 호출되는지, 또는 별도 보상 로직이 필요한지 확인.
장기 개선 (재발 방지)#
- 외부 추출 라이브러리(
@cupix/app-room-extractor-v2,@cupix/forge-agents) 의 반환 계약(nullable / throw 여부)을 타입 수준에서 강화. 현재extract의 타입이 inline literal 이라 nil 가능성을 컴파일러가 잡지 못한다.RoomExtractor.room_extract와ForgeAgent.extract가Promise<X | undefined>인지Promise<X>인지 시그니처를 확정하고 호출 측을 그에 맞춰 보강한다. - 같은 패턴(자식 IPC 결과 객체에서
.X무방비 접근)이 다른 agent (cupix-tesla-bim-revision-agent,cupix-tesla-mesh-agent,cupix-skat-master-agent등ChildProcessManager사용처) 에도 있는지 sweep —applications/agents/packages/*/src/process/*.process.ts의 결과 직조립 부분을 일괄 점검 권장.
Monitoring#
Datadog dashboard widget 용 timeseries 쿼리 (writing-datadog-monitoring-queries 가이드를 따라 monitor-only 문법은 사용하지 않음):
해당 에러 패턴 발생 건수:
sum:trace.express.request.errors{service:cupixworks-any-room-agent}.as_count()
서비스의 전체 에러 로그 건수 (log-based metric 이 설정되어 있을 경우):
sum:logs.error.count{service:cupixworks-any-room-agent}.as_count()
(uncertain — log-based metric logs.error.count 가 이 organization 에 설정되어 있는지 확인 필요. 없으면 Datadog UI 에서 service:cupixworks-any-room-agent status:error saved query 기반 monitor 로 대체.)
추가 권장 알림:
RoomService::run실패율 > 1% (5분 window) 시 page- 동일 stack frame
room-extractor.process.ts+'Levels'메시지 재발생 시 즉시 알림 (수정 효과 검증용)
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (즉시 조치: 자식 프로세스 nil guard 2곳 추가). 단기 개선의 stack 전파 개선은 standard.
- 영향 범위가 단일 BIM (20418) 이고 재시도/주변 메시지 처리에 회귀 없음. 다만 같은 종류의 input 이 다시 들어오면 동일 실패가 재현될 가능성이 있으므로 nil guard 보강은 필요.