ES /docs

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#

  1. 2026-05-15 18:43 KST — TSLA-12199 (commit 31b5b3e5b) 로 loadFloorplanfloorplanType !== Bim 가드 추가.
  2. 2026-07-16 12:02:53 KST — 배포된 ARM refinement 인스턴스에서 job 1201639 시작.
  3. 2026-07-16 12:02:53 KSTRefinementService::loadFloorplan | end - not a bim drawing, floorplan type: pdf 로그 후 Error: not a bim drawing 발생.
  4. 2026-07-16 12:02:54 KSTRefinementService::terminateService | force shutdown after 10 seconds 로 프로세스 종료.

Error Log#

Datadog Logs

text
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_typepdf 였는데, TSLA-12199 (2026-05-15) 로 도입된 가드가 FloorplanType.Bim 이 아닌 모든 타입을 명시적으로 거부하도록 만들었다. 즉 refiner 는 BIM drawing 전용 파이프라인인데, cluster 메타는 (BIM 이 아닌) 일반 PDF floorplan 을 가리키고 있었다. Tesla 쪽 CreateCaptureRefinementJobrefiner_required: capture.exist_bim_drawing? (capture 또는 level 범위에 BIM floorplan 이 하나라도 있는지) 만 검사하고 job 을 dispatch 하기 때문에, capturelevel 에 BIM floorplan 이 존재하되 cluster 가 실제로 사용한 layout 은 PDF 인 케이스에서 이 mismatch 가 노출된다.

Technical Analysis#

Code Path#

Refinement agent 실행 흐름의 실패 지점:

applications/agents/packages/cupix-capture-refinement-agent/src/refinement-service.ts:79-111typescript
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 로드 후 타입 가드에서 예외:

applications/agents/packages/cupix-capture-refinement-agent/src/refinement-service.ts:192-214typescript
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 를 얻는 방식:

applications/agents/packages/cupix-capture-refinement-agent/src/model/cpcluster.ts:74-87typescript
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 을 던짐):

app/jobs/create_capture_refinement_job.rb:44-75ruby
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 을 참조하는지는 검사하지 않는다:

app/models/concerns/refinementable.rb:87-92ruby
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:

text
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):

text
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):

text
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:54exist_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-55refiner_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 실패 시 RefinerNotFoundBimDrawing error 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 등):
text
sum:logs.hits{service:cupixworks-capture-refinement-arm-instance,status:error} by {message}.as_count()
  • "not a bim drawing" 재발 감시 timeseries:
text
sum:logs.hits{service:cupixworks-capture-refinement-arm-instance,@error.message:"not a bim drawing"}.as_count()
  • Refinement job 성공률 관측용 (info 레벨 begin/end pair):
text
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 | end info 로그가 이후에도 관측됨). 데이터/서비스 무결성에 대한 광범위한 위협은 없음.
  • 예상 복잡도: standard — tesla CreateCaptureRefinementJobrefiner_required 판정 로직에 cluster 실제 used_model 검사 추가. 관련 spec 업데이트 필요. Agent 쪽은 그대로 두어도 무방.