ES /docs

CPElement::makeElementRecords | No phases found for Element bim_external_id: 7/0/4/0/0/0/0/1, catego

RCA: CPElement::makeElementRecords | No phases found for Element

Overview#

What Happened#

2026-07-24 06:45 KST에 cupixworks-sitetrack-preprocessor-agent의 단일 실행에서 category_id: 1032584, workarea_ids: [125582]에 속한 48개 Element에 대해 CPElement::makeElementRecords가 phase를 하나도 찾지 못하고 error 레벨 로그를 남겼다. 함수는 예외를 던지지 않고 빈 _cpElementRecords를 반환하므로 전체 job은 계속 진행되지만, 해당 카테고리 요소들은 cpElementRecordMap에 등록되지 않아 이후 progress/tracking에서 누락된다.

Quick Facts#

Field Value
exception.class (no exception thrown — logger.error only)
exception.message CPElement::makeElementRecords | No phases found for Element bim_external_id: %s, category_id: %d, workarea_ids: %s
top_frame applications/agents/packages/models/src/cpelement.ts:85
env production, us-west-2
tenant cupix

Affected Teams#

Team / Domain Error Count Impact
clark-vdc (sitetrack preprocessor) 48 단일 preprocessor 실행에서 category_id: 1032584 산하 48개 element의 CPElementRecord 미생성 — 해당 요소의 progress 추적 누락 가능

Timeline#

  1. 2026-07-24 06:45:13 KSTSiteTrackPreprocessorRunner::loadAllPhases가 실행되었으나, cpPhasessrvCategoryId === 1032584인 phase가 없어 해당 CPCategory에 phase가 등록되지 않음 (sitetrack-preprocessor-runner.ts:223-224).
  2. 2026-07-24 06:45:13 KSTloadAllElementRecords가 element 순회 중 category_id: 1032584인 48개 element에 대해 makeElementRecords()를 호출하여 각각 logger.error 방출 (cpelement.ts:85). 첫/마지막 로그 시간이 동일 초(21:45:13.449Z ~ 21:45:13.463Z, 14ms 이내).

Error Log#

Datadog Logs

text
CPElement::makeElementRecords | No phases found for Element bim_external_id: 7/0/4/0/0/0/0/1, category_id: 1032584, workarea_ids: [ 125582 ]

Impact#

  • Service: cupixworks-sitetrack-preprocessor-agent
  • Team: clark-vdc
  • 발생 횟수: 48
  • 최초 발생: 2026-07-24 06:45 KST
  • 최근 발생: 2026-07-24 06:45 KST

발생 자체는 단일 preprocessor 실행 내에서 14ms 안에 완료된 48건이며, 실질적으로 하나의 category (1032584) 에 속한 elements 전부에 대한 반복 로그이다. 함수는 예외를 던지지 않고 빈 배열을 반환하므로 job 전체가 실패하지는 않지만, 이 category의 element progress는 이후 단계에서 추적되지 않는다.

Root Cause Summary#

CPElement.cpPhasesthis.cpCategory?.cpPhases를 반환한다 (cpelement.ts:60). 즉, element가 속한 CPCategory에 등록된 phase 목록이다. loadAllPhases에서는 cupixApi.phase.getAll(facilityKey)로 전체 phase를 가져온 뒤 cpPhase.srvCategoryId === cpCategory.id인 경우에만 cpCategory.addCPPhase(cpPhase)를 호출한다 (sitetrack-preprocessor-runner.ts:219-225). 이번 사건에서 category_id: 1032584에 대해 매칭되는 phase가 서버에서 0건 반환되어 cpPhases.length === 0이 되었고, makeElementRecords의 방어 분기 (cpelement.ts:84-88) 가 element별로 logger.error를 찍고 빈 배열을 반환한다. 근본 원인은 코드 결함이 아니라 facility 데이터 문제 — category 1032584에 workflow/phase가 매핑되어 있지 않거나, workflow는 있으나 phase가 정의되지 않은 상태 — 로 판단되며, 그럼에도 element 단위 loop에서 error 레벨로 로깅되는 것이 이 클러스터를 만들어냈다.

Technical Analysis#

Code Path#

Entry point: SiteTrackPreprocessorRunner.createTargetModelsloadAllPhasesloadAllElementRecordsCPElement.makeElementRecords

Phase 로딩 단계 (sitetrack-preprocessor-runner.ts:206-233):

applications/agents/packages/cupix-sitetrack-preprocessor-agent/src/runner/sitetrack-preprocessor-runner.ts:206-233typescript
async loadAllPhases(cpSitetrack: CPSitetrack, cpCategories: CPCategory[]): Promise<CPPhase[]> {
    logger.debug('SiteTrackPreprocessorRunner::createCPPhases | begin');

    if (!cpSitetrack.facilityKey) throw new Error('undefined facility_key in deviation');

    // load all phases in the facility, not only trackable phases (to make sure all element_records are created)
    const cpTasks = cpSitetrack.cpTasks;
    const cpPhases = await createModels(CPPhase, cpSitetrack, this.cupixApi.phase.getAll(cpSitetrack.facilityKey));
    if (cpPhases.length === 0) {
        this.jobManager.setErrorCode(ErrorCode.Agent.DeviationPhasesNotFound);
        throw new Error('No Phases found');
    }

    for (const cpPhase of cpPhases) {
        const cpTasksInPhase = cpTasks.filter(cpTask => cpTask.phaseId === cpPhase.id);
        cpTasksInPhase.forEach(cpTask => cpPhase.addCPTask(cpTask));

        const cpCategory = cpCategories.find(cpCategory => cpCategory.id === cpPhase.srvCategoryId);
        if (cpCategory) cpCategory.addCPPhase(cpPhase);
    }
    // ...
}

여기서 facility 전체 phase 총량이 0이면 throw가 발생하여 job이 실패하지만, 특정 category에 대해서만 매칭 phase가 없는 경우에는 조용히 넘어간다 — cpCategories.find(...)가 undefined면 그 phase는 무시되고, 반대로 어떤 category에 phase가 하나도 붙지 않아도 검증 없이 다음 단계로 넘어간다.

Element record 생성 loop (sitetrack-preprocessor-runner.ts:294-303):

applications/agents/packages/cupix-sitetrack-preprocessor-agent/src/runner/sitetrack-preprocessor-runner.ts:294-303typescript
async loadAllElementRecords(cpSitetrack: CPSitetrack, cpElements: CPElement[], uniqueLevelIds: number[]): Promise<void> {
    logger.debug('SiteTrackPreprocessorRunner::loadAllElementRecords | begin');
    if (!cpSitetrack.facilityKey) throw new Error('undefined facility_key in deviation');

    // create element records for each cpElement
    for (const cpElement of cpElements) {
        const cpElementRecords = cpElement.makeElementRecords();
        cpElementRecords.forEach(cpElementRecord => cpSitetrack.cpElementRecordMap.set(cpElementRecord.identifierKey, cpElementRecord));
    }

Failure point (cpelement.ts:83-89):

applications/agents/packages/models/src/cpelement.ts:60,83-89typescript
get cpPhases(): CPPhase[] { return this.cpCategory?.cpPhases || []; }
// ...
makeElementRecords = (): Array<CPElementRecord> => {
    if (this.cpPhases.length === 0) {
        logger.error('CPElement::makeElementRecords | No phases found for Element bim_external_id: %s, category_id: %d, workarea_ids: %s'
            , this.bimExternalId, this.categoryId, this.workareaIds);

        return this._cpElementRecords;
    }

기대 동작: element의 category에 최소 1개 이상 phase가 붙어 있어 CPElementRecord가 생성된다.

실제 동작: category 1032584 에 phase가 0개라서 방어 로그가 element별로 48번 반복 방출되고, 해당 element들은 cpElementRecordMap에 등록되지 않는다.

Log Evidence#

Datadog query (재현용):

text
service:cupixworks-sitetrack-preprocessor-agent status:error "CPElement::makeElementRecords" "category_id: 1032584"

핵심 로그 (48건 중 일부):

text
2026-07-24 06:45:13 [error] CPElement::makeElementRecords | No phases found for Element bim_external_id: 7/0/4/0/0/0/0/1, category_id: 1032584, workarea_ids: [ 125582 ]
2026-07-24 06:45:13 [error] CPElement::makeElementRecords | No phases found for Element bim_external_id: 4/1/8/0/1/3, category_id: 1032584, workarea_ids: [ 125582 ]
2026-07-24 06:45:13 [error] CPElement::makeElementRecords | No phases found for Element bim_external_id: 7/2/1/0/0/0, category_id: 1032584, workarea_ids: [ 125582 ]
2026-07-24 06:45:13 [error] CPElement::makeElementRecords | No phases found for Element bim_external_id: 7/0/4/0/0/0/0/33, category_id: 1032584, workarea_ids: [ 125582 ]

관찰 사항:

  • 48건 모두 first_seen(21:45:13.449Z)과 last_seen(21:45:13.463Z) 사이 14ms에 집중 → 단일 preprocessor run 안의 loadAllElementRecords loop.
  • 모든 로그가 동일한 category_id: 1032584, workarea_ids: [125582].
  • 다양한 bim_external_id 계열 (4/1/8/*, 7/0/4/*, 7/2/1/*) 이 등장 → 여러 BIM/Element에 걸쳐 있으나 category가 모두 같음.
  • status:warn "1032584" 로그는 동일 기간 0건 → phase 로딩 단계 자체에서는 별도 경고를 남기지 않음 (전체 facility phase가 있는 한 조용히 넘어감).

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 category_id: 1032584 에 매핑된 phase (srvCategoryId === 1032584) 가 facility 데이터에 존재하지 않음 cpelement.ts:60 cpPhasescpCategory?.cpPhases를 반환. sitetrack-preprocessor-runner.ts:223-224에서 srvCategoryId === cpCategory.id인 phase만 등록. 로그 48건 전부 동일 category. Confirmed
H2 facility 전체 phase가 0건이라 throw new Error('No Phases found') 발생 만약 그렇다면 loadAllElementRecords 단계까지 도달하지 못함 48건의 element-level error 로그가 정상적으로 방출됨 → loadAllPhases는 통과. 즉 다른 category에는 phase가 있음. Rejected
H3 Element의 cpCategory 자체가 undefined (연결 실패) 그러면 cpPhases[]가 되어 같은 분기로 감 로그에 category_id: 1032584가 정상 출력됨 (cpElement.categoryId_serverModel?.category?.idcpelement.ts:56), 즉 cpCategory는 연결되어 있고 그 category에 phase가 없는 상황 Rejected
H4 코드 배포 회귀 (recent deploy 로 phase filtering 로직이 깨짐) 발생이 단일 초/단일 category 로 격리됨. 전체 preprocessor run 이 아니라 특정 facility 데이터에서만 나타남. 다른 category (workarea_ids: [125582] 외) 는 정상 처리. Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 없음. 코드 결함이 아니라 데이터/설정 이슈로 판단됨. clark-vdc 팀에게 해당 facility 의 category_id: 1032584 (workarea_ids: [125582] 이 있는 사이트) 에 대해 workflow/phase 매핑을 검토 요청. tesla API GET /phases?facility_key=... 결과에 category 1032584 를 참조하는 phase가 있는지 확인.

단기 개선 (1주 이내)#

  • 로그 레벨/집계 완화: cpelement.ts:85 는 element-level loop 에서 호출되며 하나의 데이터 결함이 있으면 category 산하 모든 element 수만큼 error 로그가 증폭된다. 다음 중 하나를 검토:
    1. makeElementRecords 내 log level 을 warn 으로 낮추고, loadAllElementRecords 상위에서 category 별 요약 (category_id X: N elements without phases) 을 한 번만 error 로 남김.
    2. 또는 loadAllPhases 끝에 cpCategoriescpPhases.length === 0 인 category 를 미리 감지하여 한 번만 경고 후, element loop 에서는 debug 레벨로 낮춤.
  • 근거: 현재는 진짜 alert 가치가 있는 "category 하나가 phase 없이 처리됨" 이라는 단일 사건이 element 수만큼 반복되어 signal-to-noise 가 나쁘고, 자동화된 error-sweeper 가 데이터 문제를 코드 오류처럼 클러스터링하게 됨.

장기 개선 (재발 방지)#

  • Facility onboarding / configuration validation: category 를 활성화할 때 최소 1개 workflow + 1개 phase 매핑이 있는지 사전 검증. tesla 측에서 Category 생성/활성화 API 에 validation 을 추가하는 방향.
  • Preprocessor 시작 단계에서 사용될 category 전부에 대해 phase coverage matrix 를 계산하여, coverage 가 부족하면 job manager 에 warn 레벨 이벤트 로 한 번만 노출.

Monitoring#

  • No phases found for Element 발생을 category 단위로 집계하는 timeseries 추가.
text
service:cupixworks-sitetrack-preprocessor-agent status:error "CPElement::makeElementRecords" "No phases found"
  • Category 별 breakdown 을 원하는 경우 (facet 필터가 존재한다면 사용):
text
service:cupixworks-sitetrack-preprocessor-agent "CPElement::makeElementRecords" "No phases found"

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial (data-config 조치) / standard (로그 레벨 리팩터 시)