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#
- 2026-07-24 06:45:13 KST —
SiteTrackPreprocessorRunner::loadAllPhases가 실행되었으나,cpPhases중srvCategoryId === 1032584인 phase가 없어 해당CPCategory에 phase가 등록되지 않음 (sitetrack-preprocessor-runner.ts:223-224). - 2026-07-24 06:45:13 KST —
loadAllElementRecords가 element 순회 중category_id: 1032584인 48개 element에 대해makeElementRecords()를 호출하여 각각logger.error방출 (cpelement.ts:85). 첫/마지막 로그 시간이 동일 초(21:45:13.449Z ~ 21:45:13.463Z, 14ms 이내).
Error Log#
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.cpPhases는 this.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.createTargetModels → loadAllPhases → loadAllElementRecords → CPElement.makeElementRecords
Phase 로딩 단계 (sitetrack-preprocessor-runner.ts:206-233):
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):
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):
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 (재현용):
service:cupixworks-sitetrack-preprocessor-agent status:error "CPElement::makeElementRecords" "category_id: 1032584"
핵심 로그 (48건 중 일부):
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 안의
loadAllElementRecordsloop. - 모든 로그가 동일한
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 cpPhases는 cpCategory?.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?.id — cpelement.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 APIGET /phases?facility_key=...결과에 category 1032584 를 참조하는 phase가 있는지 확인.
단기 개선 (1주 이내)#
- 로그 레벨/집계 완화:
cpelement.ts:85는 element-level loop 에서 호출되며 하나의 데이터 결함이 있으면 category 산하 모든 element 수만큼 error 로그가 증폭된다. 다음 중 하나를 검토:makeElementRecords내 log level 을warn으로 낮추고,loadAllElementRecords상위에서 category 별 요약 (category_id X: N elements without phases) 을 한 번만error로 남김.- 또는
loadAllPhases끝에cpCategories중cpPhases.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 추가.
service:cupixworks-sitetrack-preprocessor-agent status:error "CPElement::makeElementRecords" "No phases found"
- Category 별 breakdown 을 원하는 경우 (facet 필터가 존재한다면 사용):
service:cupixworks-sitetrack-preprocessor-agent "CPElement::makeElementRecords" "No phases found"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (data-config 조치) / standard (로그 레벨 리팩터 시)