Cannot read properties of undefined (reading 'filepaths')
RCA: Cannot read properties of undefined (reading 'filepaths')
Overview#
What Happened#
2026-06-29 23:09:28 KST에 cupixworks-any-mesh-agent (mesh extraction agent)가 BIM id 20418 (Gilbane "Cupix_Hotel_LVL19.nwd")의 mesh 추출을 수행하려다 TypeError: Cannot read properties of undefined (reading 'filepaths') 로 실패했다. mesh extractor child process에서 추출 결과 객체가 undefined로 반환된 뒤, 결과 객체의 filepaths 필드를 접근하면서 발생했다. 1회 발생했고 BIM의 mesh_state는 error로 마감되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | TypeError |
| exception.message | Cannot read properties of undefined (reading 'filepaths') |
| top_frame | ChildProcessManager2.handleMessage (/tmp/agent/dist/app.cjs:1578:26) (bundled) — source: packages/cupix-tesla-mesh-agent/src/process/mesh-extractor.process.ts:157 |
| runtime | Node.js (bundled app.cjs, child process via fork) |
| env | production, us-west-2 |
| bim.id | 20418 |
| bim.name | Cupix_Hotel_LVL19.nwd |
| bim.forge_state | translation_skipped |
| bim.mesh_state | error (final) |
| facility.id | 12482 |
| team.id | 780 (gilbaneco) |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| gilbaneco (team.id 780) | 1 | BIM 20418의 mesh 추출 실패, mesh_state=error로 종료. 후속 mesh archive 업로드 미수행. |
Timeline#
- 2026-06-29 23:09:28 KST —
BaseService::runByMessage | id: 20418— SQS 메시지 수신, BIM 20418 처리 시작. - 2026-06-29 23:09:28 KST —
MeshService::runMeshExtractor진입, child process로 추출 요청 전송. - 2026-06-29 23:09:28 KST — child process에서 추출 실패 → IPC로 에러 응답 전달 → 부모
ChildProcessManager.handleMessage가pending.reject(new Error(response.error))호출. - 2026-06-29 23:09:28 KST —
MeshService::run | error: ... Cannot read properties of undefined (reading 'filepaths')로깅, BIM의mesh_state가error로 업데이트됨. - 2026-06-29 23:09:28 KST —
BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/20418, SQS 메시지 삭제 (AwsQueueManager::deleteMessage | end - message id: 3503c723-ddd1-4f77-b16f-900877c5ca27).
Error Log#
Cannot read properties of undefined (reading 'filepaths')
Error: Cannot read properties of undefined (reading 'filepaths')
at ChildProcessManager2.handleMessage (/tmp/agent/dist/app.cjs:1578:26)
at ChildProcess.<anonymous> (/tmp/agent/dist/app.cjs:1506: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-mesh-agent - Team: gilbaneco
- 발생 횟수: 1
- 최초 발생: 2026-06-29 23:09:28 KST
- 최근 발생: 2026-06-29 23:09:28 KST
- BIM 영향: id 20418 (
Cupix_Hotel_LVL19.nwd, facility 12482, workspace "Gilbane Bally Casino"),mesh_state=error로 마감되어 mesh 데이터가 생성되지 않음. 동일 시간대 다른 BIM (17924, 17928 등) 추출은 정상 완료.
Root Cause Summary#
cupix-tesla-mesh-agent의 mesh extractor child process가 추출 결과로 extract 객체를 만들지 못한 채 mesh-extractor.process.ts:157의 filepaths: extract.filepaths를 평가하면서 TypeError를 던졌다. BIM 20418은 forge_state: translation_skipped (Kibana 확인됨) 상태로, Forge 측 mesh 변환이 정상 완료되지 않은 BIM이다. 그럼에도 runMeshExtractor의 사전 가드 (forgeUrn, forgeFormatType, meshDirPath undefined 검사) 는 통과했고, child process 내부의 extract 변수가 양쪽 분기 중 한쪽 (legacy MeshExtractor.mesh_extract 경로) 에서 undefined로 남거나 svf2 경로가 예외를 던지지 않고 빈 결과를 만들면서 line 157의 객체 리터럴 접근이 실패한 것이 직접 원인이다. 에러는 child의 try/catch에서 catch된 뒤 IPC로 부모에게 전달되었고, 부모의 ChildProcessManager.handleMessage가 new Error(response.error)로 다시 던지면서 보고된 스택 프레임이 부모 측 handleMessage로 찍혔다.
Technical Analysis#
Code Path#
- Entry point:
packages/cupix-tesla-mesh-agent/src/mesh-service.ts:101(MeshService::run) - Validation gate:
packages/cupix-tesla-mesh-agent/src/mesh-service.ts:174-182(runMeshExtractorundefined param 가드) - IPC dispatch:
packages/cupix-tesla-mesh-agent/src/manager/mesh-extract.manager.ts:66-84(MeshExtractManager::execute) - Failure point (child):
packages/cupix-tesla-mesh-agent/src/process/mesh-extractor.process.ts:157—filepaths: extract.filepaths평가 시점에extract가undefined - Error propagation (parent):
packages/agents/base/src/manager/child-process.manager.ts:154-184(ChildProcessManager.handleMessage—pending.reject(new Error(response.error || 'Unknown error'))) - Final catch + state update:
packages/cupix-tesla-mesh-agent/src/mesh-service.ts:117-120(MeshService::runcatch →updateErrorState)
private async execute(params: ExtractorParams): Promise<ExtractorResults> {
try {
this.log('MeshExtractorProcess::execute | start');
let extract: {
filepaths: string[] | undefined;
};
if (params.formatType === 'svf2') {
// ...
const result = extractBIMModel.process();
if (result === true) {
const fileList = await CPUtils.getFiles(params.path);
extract = { filepaths: fileList };
} else {
throw new Error(`forge_bimmodel_extractor failed to extract mesh, process() returned: ${result}`);
}
} else {
extract = await MeshExtractor.mesh_extract(params.token!, params.urn, params.timeOut!, params.path);
}
const extractorResults = {
filepaths: extract.filepaths // ← 여기서 extract가 undefined면 TypeError
};
this.log('MeshExtractorProcess::execute | Mesh extraction completed successfully');
return extractorResults;
} catch (error) {
// ...
throw error;
}
}
private runMeshExtractor = async (cpBim: CPBim): Promise<void> => {
logger.debug('MeshService::runMeshExtractor | begin');
if (cpBim.forgeUrn == undefined
|| cpBim.forgeFormatType == undefined
|| cpBim.meshDirPath == undefined
|| Environment.CPX_EXTRACTOR_TIMEOUT == undefined
) {
throw new Error('MeshService::runMeshExtractor | end - undefined params');
}
const meshExtractorResults = await this.meshExtractManager.execute({
apiConfig:{
clientId: Environment.ADF_CLIENT_ID,
clientSecret: Environment.ADF_CLIENT_SECRET
},
urn: cpBim.forgeUrn,
path: cpBim.meshDirPath,
timeOut: Environment.CPX_EXTRACTOR_TIMEOUT,
formatType: cpBim.forgeFormatType,
region: cpBim.forgeRegion
});
cpBim.fromMeshExtractor(meshExtractorResults);
logger.debug('MeshService::runMeshExtractor | end');
};
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')); // ← 부모 스택의 보고 지점
}
}
기대 동작: child의 MeshExtractor.mesh_extract 또는 svf2 추출이 항상 { filepaths: string[] | undefined } 형태 객체를 반환하거나, 실패 시 명시적 Error를 던져야 한다.
실제 동작: legacy 분기 (params.formatType !== 'svf2')에서 MeshExtractor.mesh_extract(params.token!, ...)이 호출되는데, MeshExtractManager.execute가 보내는 params에는 token 필드가 아예 없다 (apiConfig.clientId/clientSecret만 있고 token은 전달 안 됨). 따라서 legacy native lib에 token=undefined가 전달되어 native 측에서 빈/undefined 반환이 가능하고, 그 경우 extract가 undefined인 채로 line 157이 평가되며 TypeError가 던져진다. (또는 svf2 측에서 result === true도 아니고 Error도 throw되지 않는 변환된 native 경로가 있다면 동일한 증상이 가능하다 — uncertain.)
Log Evidence#
Datadog query:
service:cupixworks-any-mesh-agent status:error
from 2026-06-29T14:08:00Z to 2026-06-29T14:11:00Z
해당 BIM 처리 사이클의 실제 로그 (시간순):
2026-06-29 23:09:28 info BaseService::runByMessage | id: 20418
2026-06-29 23:09:28 info CupixAuth::setSession | session_id: be789844db7da3a2eb3c1b42f86116929d1ee5fe
2026-06-29 23:09:28 info MeshService::runMeshExtractor | forge_bimmodel_extractor version: 1.1.2
2026-06-29 23:09:28 error Cannot read properties of undefined (reading 'filepaths')
2026-06-29 23:09:28 error MeshService::run | error: {"stack":"Error: Cannot read properties of undefined (reading 'filepaths')\n at ChildProcessManager2.handleMessage (/tmp/agent/dist/app.cjs:1578:26)\n at ChildProcess.<anonymous> (/tmp/agent/dist/app.cjs:1506:16)\n ...","message":"Cannot read properties of undefined (reading 'filepaths')"}
2026-06-29 23:09:28 info BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/20418
2026-06-29 23:09:28 info AwsQueueManager::deleteMessage | end - message id: 3503c723-ddd1-4f77-b16f-900877c5ca27
주목할 점: BIM 20418 처리 사이클에는 MeshExtractorProcess::execute | start / process() result / MeshService::uploadBimMeshArchiveFile | end 같은 정상 완료 로그가 전혀 없고, MeshService::runMeshExtractor 진입 직후 바로 에러가 보고된다. 동일 분 (23:09:05–23:09:17)에 처리된 BIM 17924, 17928은 MeshService::uploadBimMeshArchiveFile | end로 정상 완료되어 있어, 인프라/리전 전반 장애가 아니라 특정 BIM 20418에 한정된 데이터 종속 문제임을 시사한다.
Kibana로 확인한 BIM 20418 상태 (production-us, bims index):
{
"id": 20418,
"uuid": "11cee9d5-8844-5f7b-a27d-64d377e91a20",
"name": "Cupix_Hotel_LVL19.nwd",
"state": "done",
"resource_state": "created",
"forge_state": "translation_skipped",
"room_state": "error",
"mesh_state": "error",
"fresh_state": "stale",
"error_code": null,
"state_updated_at": "2026-06-29T14:09:21.000Z",
"user": { "id": 36237, "email": "crose@gilbaneco.com" },
"workspace": { "id": 3947, "name": "Gilbane Bally Casino" }
}
forge_state: translation_skipped는 Forge translation 단계가 건너뛰어진 BIM임을 의미한다. Kibana 인덱스에는 forge_urn / forge_format_type 컬럼이 노출되지 않아 정확한 값은 확인 불가 (uncertain — needs verification via Tesla API), 그러나 runMeshExtractor의 가드를 통과한 것으로 보아 이 두 필드는 null/undefined가 아닌 값을 가지고 있었음.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Mesh extractor child process가 추출 결과(extract)를 만들지 못한 채 mesh-extractor.process.ts:157에서 extract.filepaths 접근 시 TypeError. legacy 경로 (MeshExtractor.mesh_extract)가 token=undefined로 호출되어 undefined 반환했거나, 결과 객체가 형태를 갖추지 못함. |
Source: mesh-extractor.process.ts:118-158 (let extract: {...} 선언, line 157 filepaths: extract.filepaths). Source: MeshExtractManager.execute (manager line 184-194)에서 token 필드 미전달. Kibana: BIM 20418 forge_state: translation_skipped → Forge translation이 정상 완료되지 않은 BIM. Datadog: MeshService::runMeshExtractor 직후 즉시 에러, 다른 BIM은 동일 시간 정상 완료. |
— | Confirmed |
| H2 | cpbim.ts:156 (this.meshExtractorResults?.filepaths.length) 에서 발생한 parent-side dereference 에러. |
Source에 .filepaths 접근이 존재. |
메시지 텍스트가 "reading 'filepaths'" — 만약 meshExtractorResults가 undefined면 optional chaining이 단락되어 에러 발생 안 하고, {filepaths: undefined} 였다면 메시지가 "reading 'length'"가 되어야 함. 일치 안 함. |
Rejected |
| H3 | ChildProcessManager.handleMessage 자체가 .filepaths를 접근. |
Stack trace 최상단 frame이 ChildProcessManager2.handleMessage. |
Source (child-process.manager.ts:154-184)에 .filepaths 직접 접근 없음. pending.reject(new Error(response.error))로 child의 IPC 에러 메시지를 그대로 다시 던진 결과로, stack frame이 부모 측 handleMessage에 찍힌 것. |
Rejected |
| H4 | Forge / Autodesk 외부 API 장애. | — | Datadog에 외부 API 5xx/timeout 로그 없음. 같은 시간 다른 BIM은 정상 처리 완료. status board에 dep:* incident 없음. | Rejected |
| H5 | 입력 SQS 메시지가 손상되어 params가 child에 빈 객체로 도달. |
— | Source: MeshExtractManager.execute가 항상 apiConfig/urn/path/formatType 등을 명시적으로 채워 보냄. runMeshExtractor 가드를 통과했으므로 forgeUrn, forgeFormatType 모두 정의됨. |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
packages/cupix-tesla-mesh-agent/src/process/mesh-extractor.process.ts:152-158의 legacy 분기와 결과 dereference를 방어한다.- legacy 분기에서
extract = await MeshExtractor.mesh_extract(...)결과가null/undefined이거나filepaths가 없으면 명시적Error('legacy mesh_extract returned no result')를 던진다. - line 157의
filepaths: extract.filepaths를 가드된 변수 (extract?.filepaths) 또는 위 검사 후로 옮긴다.
- legacy 분기에서
packages/cupix-tesla-mesh-agent/src/mesh-service.ts:174-197에서 BIM의forge_state === 'translation_skipped'또는forge_format_type이 지원 형식 (svf2/svf)가 아닐 때 child process까지 가지 말고 가드에서 조기에updateErrorState로 분기한다. 현재 가드는== undefined만 체크하므로forge_state: translation_skipped같은 비유효 BIM을 잡지 못한다.- 근거: BIM 20418은 Forge 변환이 건너뛰어진 상태(
translation_skipped)였음에도 mesh 추출 SQS 메시지가 큐에 들어와 처리됨. 입력 정합성 확보가 우선.
단기 개선 (1주 이내)#
- legacy
MeshExtractor.mesh_extract호출 경로의token파라미터 미전달 정합성 점검.MeshExtractManager.execute의MeshExtractorParams에는 token 필드가 없는데 (mesh-extract.manager.ts:110-126), child의 ExtractorParams에는token필드가 있음 (mesh-extractor.process.ts:31-41). 두 인터페이스 사이 계약이 어긋남 — legacy 경로가 실제로 사용된다면 Forge 인증 토큰을 명시적으로 전달하도록runMeshExtractor를 보완하거나, legacy 경로 자체를 제거하고 svf2 전용으로 좁힌다. - mesh 추출 enqueue 측 (Tesla API/worker)에서
forge_state검증:translation_skipped/error상태에서는 mesh agent 큐로 메시지를 보내지 않도록 한다.
장기 개선 (재발 방지)#
- child process IPC 응답에 대해 zod (혹은 유사) 스키마 검증을 추가해
{ filepaths: string[] }형태가 아니면 부모 측에서 명시적 ValidationError를 던진다. 이렇게 하면 native 라이브러리가 형태가 다른 값을 반환해도 익명의 TypeError 대신 의미 있는 진단 메시지가 남는다. - 번들된 child
dist/app.cjs에 sourcemap을 첨부해 production stack trace가 source의 file:line으로 해석되도록 한다. 현재는 bundled stack(/tmp/agent/dist/app.cjs:1578)으로 디버깅 시간이 늘어남.
Monitoring#
새 모니터링 쿼리 (Datadog dashboard timeseries에 그대로 사용 가능):
service:cupixworks-any-mesh-agent status:error "Cannot read properties of undefined"
service:cupixworks-any-mesh-agent status:error "MeshService::run"
service:cupixworks-any-mesh-agent "forge_bimmodel_extractor failed to extract mesh"
cupixworks-any-mesh-agent의 모든status:error카운트가 일정 임계 (예: 5분간 5건) 이상이면 알림.- 추가로 worker 측에서
forge_state별 mesh enqueue 건수를 메트릭으로 노출해,translation_skipped상태의 BIM이 enqueue되는지 추적한다.
Risk Assessment#
- Risk level: low (1건, 단일 BIM, 다른 BIM 처리에 영향 없음)
- 예상 복잡도: standard (mesh-extractor.process.ts 가드 추가 + mesh-service.ts 사전 검증, 코드 변경 범위가 좁고 단위 테스트 작성 가능. legacy 경로 정리는 별도 단기 작업으로 분리 권장)