ES /docs

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_stateerror로 마감되었다.

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#

  1. 2026-06-29 23:09:28 KSTBaseService::runByMessage | id: 20418 — SQS 메시지 수신, BIM 20418 처리 시작.
  2. 2026-06-29 23:09:28 KSTMeshService::runMeshExtractor 진입, child process로 추출 요청 전송.
  3. 2026-06-29 23:09:28 KST — child process에서 추출 실패 → IPC로 에러 응답 전달 → 부모 ChildProcessManager.handleMessagepending.reject(new Error(response.error)) 호출.
  4. 2026-06-29 23:09:28 KSTMeshService::run | error: ... Cannot read properties of undefined (reading 'filepaths') 로깅, BIM의 mesh_stateerror로 업데이트됨.
  5. 2026-06-29 23:09:28 KSTBaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/20418, SQS 메시지 삭제 (AwsQueueManager::deleteMessage | end - message id: 3503c723-ddd1-4f77-b16f-900877c5ca27).

Error Log#

Datadog Logs

text
Cannot read properties of undefined (reading 'filepaths')
MeshService::run | error stacktext
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:157filepaths: 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.handleMessagenew 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 (runMeshExtractor undefined 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:157filepaths: extract.filepaths 평가 시점에 extractundefined
  • Error propagation (parent): packages/agents/base/src/manager/child-process.manager.ts:154-184 (ChildProcessManager.handleMessagepending.reject(new Error(response.error || 'Unknown error')))
  • Final catch + state update: packages/cupix-tesla-mesh-agent/src/mesh-service.ts:117-120 (MeshService::run catch → updateErrorState)
packages/cupix-tesla-mesh-agent/src/process/mesh-extractor.process.ts:118-161typescript
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;
    }
}
packages/cupix-tesla-mesh-agent/src/mesh-service.ts:174-197typescript
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');
};
packages/agents/base/src/manager/child-process.manager.ts:154-184typescript
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 반환이 가능하고, 그 경우 extractundefined인 채로 line 157이 평가되며 TypeError가 던져진다. (또는 svf2 측에서 result === true도 아니고 Error도 throw되지 않는 변환된 native 경로가 있다면 동일한 증상이 가능하다 — uncertain.)

Log Evidence#

Datadog query:

text
service:cupixworks-any-mesh-agent status:error
from 2026-06-29T14:08:00Z to 2026-06-29T14:11:00Z

해당 BIM 처리 사이클의 실제 로그 (시간순):

Datadog (KST)text
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):

Kibana bims/_doc/20418 (excerpt)json
{
  "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'" — 만약 meshExtractorResultsundefined면 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) 또는 위 검사 후로 옮긴다.
  • 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.executeMeshExtractorParams에는 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에 그대로 사용 가능):

text
service:cupixworks-any-mesh-agent status:error "Cannot read properties of undefined"
text
service:cupixworks-any-mesh-agent status:error "MeshService::run"
text
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 경로 정리는 별도 단기 작업으로 분리 권장)