not a bim drawing
RCA: not a bim drawing
Overview#
What Happened#
cupixworks-capture-refinement-arm-instance (ARM ECS task, refinement agent for capture 3D reconstruction) 가 2026-07-16 12:02:53 KST 에 job 1201639 실행 중 RefinementService::loadFloorplan 에서 Error: not a bim drawing 을 던지고 종료했다. 클러스터 메타데이터의 refinement_layout.used_model 이 가리키는 floorplan 이 BIM 타입이 아닌 pdf 타입이었기 때문에 TSLA-12199 (2026-05-15) 에서 도입된 타입 가드가 작동했다. 1건 단발 오류이며 다른 refinement 작업은 정상.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Error |
| exception.message | not a bim drawing |
| top_frame | applications/agents/packages/cupix-capture-refinement-agent/src/refinement-service.ts:203 |
| deploy | 31b5b3e5b (TSLA-12199, 2026-05-15) |
| env | production, us-west-2 |
| tenant | cupix |
| job_id | 1201639 |
| floorplan_type (observed) | pdf |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| weoneil (capture refinement) | 1 | 단일 capture 의 refinement job 실패 — refinement_state 가 refined 로 승격되지 못함 |
Timeline#
- 2026-05-15 18:43 KST — TSLA-12199 (commit
31b5b3e5b) 로loadFloorplan에floorplanType !== Bim가드 추가. - 2026-07-16 12:02:53 KST — 배포된 ARM refinement 인스턴스에서 job
1201639시작. - 2026-07-16 12:02:53 KST —
RefinementService::loadFloorplan | end - not a bim drawing, floorplan type: pdf로그 후Error: not a bim drawing발생. - 2026-07-16 12:02:54 KST —
RefinementService::terminateService | force shutdown after 10 seconds로 프로세스 종료.
Error Log#
not a bim drawing
Impact#
- Service:
cupixworks-capture-refinement-arm-instance - Team: weoneil
- 발생 횟수: 1
- 최초 발생: 2026-07-16 12:02:53 KST
- 최근 발생: 2026-07-16 12:02:53 KST
Root Cause Summary#
Refinement agent 는 capture 의 selected_unrefined_cluster 에서 refinement_layout.used_model.id 를 읽어 floorplan 을 로드한다. 이번 job 1201639 에서는 그 floorplan 의 floorplan_type 이 pdf 였는데, TSLA-12199 (2026-05-15) 로 도입된 가드가 FloorplanType.Bim 이 아닌 모든 타입을 명시적으로 거부하도록 만들었다. 즉 refiner 는 BIM drawing 전용 파이프라인인데, cluster 메타는 (BIM 이 아닌) 일반 PDF floorplan 을 가리키고 있었다. Tesla 쪽 CreateCaptureRefinementJob 는 refiner_required: capture.exist_bim_drawing? (capture 또는 level 범위에 BIM floorplan 이 하나라도 있는지) 만 검사하고 job 을 dispatch 하기 때문에, capture 나 level 에 BIM floorplan 이 존재하되 cluster 가 실제로 사용한 layout 은 PDF 인 케이스에서 이 mismatch 가 노출된다.
Technical Analysis#
Code Path#
Refinement agent 실행 흐름의 실패 지점:
private run = async (): Promise<void> => {
logger.info('RefinementService::run | begin');
try {
const srvJob = await this.jobManager.loadJob(Environment.CPX_JOB_ID as number);
await this.jobManager.updateRunningActionJob('refinement');
const cpCapture = srvJob ? await this.createCPCaptureByJob(srvJob) : undefined;
if (cpCapture) {
this.checkInputFiles();
await this.loadEntityParameter(cpCapture);
await this.loadCaptureResources(cpCapture);
await this.loadCluster(cpCapture);
await this.loadFloorplan(cpCapture); // ← 여기서 throw
...
Failure point — floorplan 로드 후 타입 가드에서 예외:
private loadFloorplan = async (cpCapture: CPCapture): Promise<void> => {
logger.debug('RefinementService::loadFloorplan | begin');
const floorplanId = cpCapture.cpCluster?.getUsedFloorplanIdFromMeta();
if (floorplanId == undefined) {
logger.error('RefinementService::loadFloorplan | end - not found floorplan id');
this.jobManager.setErrorCode(ErrorCode.ScenemapperUtils.RefinerNotFoundFloorplan);
throw new Error('not found floorplan id');
}
const srvFloorplan = await this.cupixApi.floorplan.get(floorplanId);
logger.debug('RefinementService::loadFloorplan | floorplan id: %d, floorplan name: %s', srvFloorplan.id, srvFloorplan.name);
const newCPFloorplan = new CPFloorplan(srvFloorplan, cpCapture);
if (newCPFloorplan.floorplanType !== TESLA.FloorplanType.Bim) {
logger.error('RefinementService::loadFloorplan | end - not a bim drawing, floorplan type: %s', newCPFloorplan.floorplanType);
this.jobManager.setErrorCode(ErrorCode.ScenemapperUtils.RefinerNotFoundBimDrawing);
throw new Error('not a bim drawing');
}
cpCapture.setCPFloorplan(newCPFloorplan);
...
Cluster 메타에서 floorplan id 를 얻는 방식:
getUsedFloorplanIdFromMeta = (): number | undefined => {
if (this.meta == undefined) {
logger.warn('CPCluster::getUsedFloorplanIdFromMeta | meta is undefined');
return;
}
const usedModel = (<any>this.meta)?.refinement_layout?.used_model;
if (usedModel.type != 'floorplan') {
logger.warn('CPCluster::getUsedFloorplanIdFromMeta | used model type is not floorplan');
return;
}
return usedModel.id;
};
Tesla 쪽 dispatch (capture 자체 또는 level scope 에 BIM 이 있으면 job 을 던짐):
def invoke_function
capture = self.jobable
...
payload = {
transform_data: {
capture_id: capture.id,
team_id: capture.team_id,
refiner_required: capture.exist_bim_drawing?
},
...
exist_bim_drawing? 는 capture 자체의 floorplan 이 BIM 이거나, level 하위에 하나라도 published BIM 이 있으면 true 를 반환한다. cluster 의 used_model 이 실제로 어떤 floorplan 을 참조하는지는 검사하지 않는다:
def exist_bim_drawing?
return true if self.floorplan&.floorplan_type == 'bim' && self.floorplan&.cycle_state == 'created' && self.floorplan&.state == 'done'
return true if self.level_id.present? && self.level.floorplans.where(floorplan_type: 'bim', cycle_state: 'created', state: 'done').exists?
false
end
기대 동작 vs 실제 동작: dispatch 조건은 "BIM floorplan 이 어딘가에 존재"만 확인하지만, refinement agent 는 cluster.refinement_layout.used_model 이 가리키는 floorplan 이 반드시 BIM 이어야 한다고 가정한다. cluster 가 PDF (non-BIM) layout 을 참조하는 경우 두 조건 사이에 gap 이 생겨 agent 가 조기 종료된다.
Log Evidence#
Datadog query:
service:cupixworks-capture-refinement-arm-instance status:error "not a bim drawing"
Job 1201639 실행 시퀀스 (2026-07-16 03:02:53 UTC / 12:02:53 KST):
12:02:53 info RefinementService::init
12:02:53 info RefinementService::authenticate | begin
12:02:53 info CupixAuth::setSession | session_id: cc0cdd0e949de8f722afc00c0591abc75a896b57
12:02:53 info RefinementService::authenticate | end
12:02:53 info RefinementService::run | begin
12:02:53 info JobManager::loadJob | begin - job id: 1201639
12:02:53 info JobManager::loadJob | end - job id: 1201639
12:02:53 error RefinementService::loadFloorplan | end - not a bim drawing, floorplan type: pdf
12:02:53 error not a bim drawing
12:02:54 info RefinementService::terminateService | force shutdown after 10 seconds
동일 서비스의 다른 최근 에러들과 비교하면 이 시그니처는 이 시점에만 나타난 단발 이벤트이다 (지난 24h 내 이 클러스터에 속하는 발생: 2건, 모두 12:02:53 KST 의 동일 job):
2026-07-16 04:56:46 Process killed by signal SIGABRT (별도 클러스터)
2026-07-16 09:04:28 not found refined align_preview_meta_refinement_with_prior_map.json (별도 클러스터)
2026-07-16 12:02:53 RefinementService::loadFloorplan | end - not a bim drawing, floorplan type: pdf ← this cluster
2026-07-16 12:02:53 not a bim drawing ← this cluster
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Cluster 의 refinement_layout.used_model 이 non-BIM (PDF) floorplan 을 참조하는데도 tesla 가 refinement job 을 dispatch 해서 agent 의 BIM-only 가드가 발동 |
Datadog 로그의 floorplan type: pdf; refinement-service.ts:200-203 의 명시적 가드; create_capture_refinement_job.rb:54 는 exist_bim_drawing? (capture/level scope) 만 검사, used_model 은 검사하지 않음 |
— | Confirmed |
| H2 | 최근 배포/코드 변경이 정상 흐름을 깨서 에러가 신규 도입됨 | — | 가드 추가 commit 31b5b3e5b 은 2026-05-15 (2개월 이상 전) 로 이번 이벤트와 시점 무관; 같은 서비스에서 대량 재발 아님 (occurrence_count=1) |
Rejected |
| H3 | Floorplan API (floorplan.get(floorplanId)) 가 잘못된 리소스를 반환 (DB 데이터 손상) |
이론적으로 가능 | 로그의 floorplan id / name 이 유효하게 로드됨을 확인하는 debug 라인은 Datadog 에 없으나(debug 미저장), 에러 메시지는 "type: pdf" 로 명시적 — API 응답 자체가 손상되었다면 파싱 단계에서 다른 예외가 났을 것 |
Rejected |
| H4 | 외부 dependency 장애 (S3/EFS/Cognito) 로 인한 우발 실패 | — | status-board 에서 active: null, recent: []; 실패 지점은 순수 in-process 타입 비교 |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
- Tesla 쪽 dispatch gate 를 실제 cluster 사용 layout 과 일치시키기 —
app/jobs/create_capture_refinement_job.rb:44-55의refiner_required: capture.exist_bim_drawing?는 capture/level scope 를 훑기 때문에, cluster 의refinement_layout.used_model이 PDF 인 케이스를 걸러내지 못한다. dispatch 시점에capture.selected_unrefined_cluster.refinement_layout.used_model의 실제 floorplan 을 조회해 BIM 여부를 확인하도록 조건을 좁힐 것. 최소한 non-BIM 이 확인되면 SQS 로 보내지 말고 job 을 성공적으로 skip 처리해야 한다. - 대안 (agent 쪽):
refinement-service.ts:200-203에서 non-BIM 감지 시throw대신warn로그 후 정상 종료로 처리하고updateCompleteActionJob('refinement', Stopped)로 job 을 완료 처리. 단, 이는 refinement 를 실제로 수행하지 않고도 성공으로 보이게 만들 수 있어 dispatch-side 수정이 root-cause 관점에서 더 정확하다.
단기 개선 (1주 이내)#
- Cluster 의
refinement_layout.used_model이 어떻게 채워지는지 (어느 서비스/워커가 PDF 를 골랐는지) 상류를 조사. 만약 사용자가 refine 대상으로 PDF 를 선택할 수 있는 UI 경로가 남아 있으면 그 UI/API 에서도 BIM 만 허용하도록 정리. - Refinement job 실패 시
RefinerNotFoundBimDrawingerror code 를 사용자 노출 문구와 매핑해 "cluster 가 BIM 이 아닌 floorplan 을 참조 중" 임을 알 수 있게 한다 (packages/utils/src/error-code/scenemapper.ts확인 필요).
장기 개선 (재발 방지)#
- Capture / Cluster / Floorplan 간 "refinement 가능 여부" 를 단일 소스에서 계산하는 서비스 오브젝트 도입. 현재는 tesla (
exist_bim_drawing?) 와 agent (floorplanType !== Bim) 두 곳에서 서로 다른 기준으로 판단해 정합성이 깨지고 있다. - Cluster 마이그레이션:
refinement_layout.used_model.id가 non-BIM floorplan 을 가리키는 기존 레코드 backfill / cleanup.
Monitoring#
- Refinement 실패 유형별 카운트 dashboard (BIM mismatch, missing input, SIGABRT 등):
sum:logs.hits{service:cupixworks-capture-refinement-arm-instance,status:error} by {message}.as_count()
- "not a bim drawing" 재발 감시 timeseries:
sum:logs.hits{service:cupixworks-capture-refinement-arm-instance,@error.message:"not a bim drawing"}.as_count()
- Refinement job 성공률 관측용 (info 레벨 begin/end pair):
sum:logs.hits{service:cupixworks-capture-refinement-arm-instance,"RefinementService::run | end"}.as_count()
Risk Assessment#
- Risk level: low — 지난 24h 내 단 1건, 다른 refinement job 은 정상 진행 (
RefinementService::run | endinfo 로그가 이후에도 관측됨). 데이터/서비스 무결성에 대한 광범위한 위협은 없음. - 예상 복잡도: standard — tesla
CreateCaptureRefinementJob의refiner_required판정 로직에 cluster 실제 used_model 검사 추가. 관련 spec 업데이트 필요. Agent 쪽은 그대로 두어도 무방.