ES /docs

BimRevisionService::run | error: "BimRevisionService::runBimCompare | end - validation failed (error

RCA: BimRevisionService runBimCompare validation failed (errorCode: REV141)

Overview#

What Happened#

2026-06-24 23:07 KST에 cupixworks-any-bimrevision-agent (us-west-2) 가 bimRevisionId: 24612 (BIM 19655, facility drdm2z, V1V2) 의 BIM 리비전 비교를 실행하던 중 runBimCompare 단계에서 REV141 (BimRevisionExcessiveRemoval) 검증 실패로 종료되었다. Forge 비교 결과 제거 비율이 26.0% 로 facility 에 설정된 임계값 15% 를 초과하여 안전 장치가 발동했고, 해당 리비전 1건의 비교가 Error 상태로 마감되었다.

Quick Facts#

Field Value
exception.class Error (generic, thrown in runBimCompare)
exception.message BimRevisionService::runBimCompare | end - validation failed (errorCode: REV141)
top_frame packages/cupix-tesla-bim-revision-agent/src/bim-revision-service.ts:690
error_code REV141 (BimRevisionExcessiveRemoval, AGT* family)
agent_version 10.98.0
env production, region us-west-2

Affected Teams#

Team / Domain Error Count Impact
exyte (BIM revision pipeline, facility drdm2z) 1 BIM 리비전 비교가 실패하여 bim_comparison_stateError 로 기록됨. 24시간 창에서는 동일 클러스터 1건이지만, 14일 창에서 동일 메시지(REV141) 가 6건 발생하여 BIM 별 반복 사례가 존재.

Timeline#

  1. 2026-06-24 23:06:45 KSTBaseService::runByMessage | id: 24612 — SQS 메시지 수신, 리비전 처리 시작
  2. 2026-06-24 23:06:45 KSTBimRevisionService::loadAllElements | begin - bimId: 19655, facilityKey: drdm2z — 기존 element 로드 시작
  3. 2026-06-24 23:06:47 KSTBimRevisionService::REVISE-BEGINcp_elements_count: 77, cp_elements_created_count: 77, cp_elements_deleted_count: 0, V1(prev) → V2(src)
  4. 2026-06-24 23:07:04 KSTCPBimRevision::validateBimComparerResult | excessive removal detected - bimRevisionId: 24612, bimId: 19655, trigger: ratio 26.0% >= 15%
  5. 2026-06-24 23:07:04 KSTBimRevisionService::run | error: "BimRevisionService::runBimCompare | end - validation failed (errorCode: REV141)" (cluster representative log)
  6. 2026-06-24 23:07:04 KSTBimRevisionService::REVISE-ENDelapsed_ms: 19388, status: "error", 모든 counts 0

Error Log#

Datadog Logs

text
BimRevisionService::run | error: "BimRevisionService::runBimCompare | end - validation failed (errorCode: REV141)"

Impact#

  • Service: cupixworks-any-bimrevision-agent
  • Team: exyte
  • 발생 횟수: 1 (cluster 단위)
  • 최초 발생: 2026-06-24 23:07 KST
  • 최근 발생: 2026-06-24 23:07 KST
  • 누적 (14일): 동일 REV141 메시지가 6건 더 관측됨 (bim_id: 17446 2건, bim_id: 2760 3건, bim_id: 19655 1건). 즉 단발이 아니라 facility 별로 반복 발생하는 패턴.

Root Cause Summary#

BimRevisionService.runBimCompare 가 forge-agents 자식 프로세스의 비교 결과를 CPBimRevision.validateBimComparerResult 에 넘겼고, 그 안의 hasExcessiveRemoval 가드가 “이번 리비전에서 제거된 element 비율이 facility 임계값을 넘었다”고 판단해 의도적으로 false 를 반환했다. 이번 케이스는 facility drdm2zbim_revision_removed_ratio_threshold 가 15(%) 로 설정되어 있고, Forge 비교가 26.0% 의 제거를 보고하여 가드가 발동한 것이다. bypassThresholdfalse 였기 때문에 _validationErrorCode = REV141 이 세팅된 채 검증 실패로 처리되어 상위에서 throw new Error(... errorCode: REV141) 로 정상적인 안전 정지 흐름을 탔다. 즉 코드 결함이 아니라 데이터 안전 가드의 정상 동작이며, 실제로 의문스러운 대량 제거가 일어난 것인지 vs. 임계값이 너무 보수적인지에 대한 비즈니스 판단이 root cause 결정의 핵심이다.

Technical Analysis#

Code Path#

  • Entry point: packages/cupix-tesla-bim-revision-agent/src/bim-revision-service.ts:150 (run)
  • runrunBimCompare 호출 (bim-revision-service.ts:177)
  • runBimCompare 가 forge 자식 프로세스 결과(bimCompareResults) 를 받아 cpBimRevision.validateBimComparerResult(bimCompareResults, threshold) 호출 (bim-revision-service.ts:680)
  • 검증 실패 시 throw (bim-revision-service.ts:687-690)
  • 검증 실패는 hasExcessiveRemoval 가드에서 발생 (packages/cupix-tesla-bim-revision-agent/src/model/cpbim-revision.ts:177-179, :234-265)
  • 외부 try/catch 가 잡아서 cluster representative 로그를 출력하고 bim_comparison_stateError 로 갱신 (bim-revision-service.ts:188-191)
  • Failure point: model/cpbim-revision.ts:260 (_validationErrorCode = ErrorCode.Agent.BimRevisionExcessiveRemoval)
packages/cupix-tesla-bim-revision-agent/src/bim-revision-service.ts:680-691typescript
if (cpBimRevision.validateBimComparerResult(bimCompareResults, threshold)) {
    this.updateBimModelsFromComparison(cpBimRevision, report);

    if (!cpBimRevision.saveBimRevisionResults()) {
        this.setErrorCode(ErrorCode.Agent.BimRevisionFailedToSaveResult);
        throw new Error('BimRevisionService::runBimCompare | end - failed to save bim compare results');
    }
} else {
    const validationErrorCode = cpBimRevision.validationErrorCode ?? ErrorCode.Agent.BimRevisionExtractorInvalidResult;
    this.setErrorCode(validationErrorCode);
    throw new Error(`BimRevisionService::runBimCompare | end - validation failed (errorCode: ${validationErrorCode})`);
}
packages/cupix-tesla-bim-revision-agent/src/model/cpbim-revision.ts:176-179typescript
if (report) {
    if (this.hasExcessiveRemoval(report, cpBim, threshold)) {
        return false;
    }
packages/cupix-tesla-bim-revision-agent/src/model/cpbim-revision.ts:234-265typescript
private hasExcessiveRemoval = (report: ForgeAgent.Report, cpBim: CPBim, threshold: BimRevisionThreshold): boolean => {
    const forgeRemovedCount = report.removed.length;
    const forgeModifiedCount = report.modified.length;
    const forgeExistCount = report.exist.length;
    const elementCreatedCount = cpBim.cpElementsCreated.length;
    const elementDeletedCount = cpBim.cpElementsDeleted.length;
    const elementTotalCount = cpBim.cpElements.size;
    const removedRatio = elementCreatedCount > 0 ? (forgeRemovedCount / elementCreatedCount) * 100 : 0;

    if (forgeRemovedCount >= threshold.countThreshold || (elementCreatedCount > 0 && removedRatio >= threshold.ratioThreshold)) {
        const logLevel = threshold.bypassThreshold ? 'warn' : 'error';
        const trigger = forgeRemovedCount >= threshold.countThreshold ? `count ${forgeRemovedCount} >= ${threshold.countThreshold}` : `ratio ${removedRatio.toFixed(1)}% >= ${threshold.ratioThreshold}%`;
        // ... logger[logLevel] (...)
        if (threshold.bypassThreshold) {
            logger.warn('CPBimRevision::validateBimComparerResult | bypass enabled, continuing');
            return false;
        }

        this._validationErrorCode = ErrorCode.Agent.BimRevisionExcessiveRemoval;
        return true;
    }

    return false;
};
packages/cupix-tesla-bim-revision-agent/src/bim-revision-service.ts:188-191typescript
} catch (ec: any) {
    logger.error('BimRevisionService::run | error: %s', JSON.stringify(ec.message));
    await this.updateBimRevisionBimComparisonState(targetId, TESLA.UpdateBimRevisionRequest.BimComparisonStateEnum.Error, this.errorCode);
}

기대 동작은 “Forge 비교에서 제거 비율이 facility 가 정한 안전 한계를 넘으면 자동 적용을 중단하고 사람의 확인을 요구한다”이고, 실제 로그가 보여주는 동작도 정확히 그것이다. 즉 throw 자체는 의도된 안전 정지(graceful safety abort) 이며, 호출부가 이를 cluster 의 error 레벨로 기록한 것이 error-sweeper 에 잡힌 원인이다.

Log Evidence#

쿼리 (재현용):

text
service:cupixworks-any-bimrevision-agent "REV141"
text
service:cupixworks-any-bimrevision-agent "excessive removal"
text
service:cupixworks-any-bimrevision-agent (24612 OR 19655)

발생 시점 전후 핵심 로그 (KST 변환):

json
{"timestamp":"2026-06-24 23:06:45 KST","status":"info","message":"BaseService::runByMessage | id: 24612"}
json
{"timestamp":"2026-06-24 23:06:47 KST","status":"info","message":"BimRevisionService::REVISE-BEGIN | {\"bim_id\":19655,\"src_revision_id\":24612,\"src_revision_name\":\"V2\",\"prev_revision_id\":23776,\"prev_revision_name\":\"V1\",\"agent_version\":\"10.98.0\",\"si_trace_id\":\"bcddea93-45fb-4478-93b0-fccc9d6b0c7f\",\"cp_elements_count\":77,\"cp_elements_created_count\":77,\"cp_elements_deleted_count\":0,\"src_forge_urn\":\"dXJuOmFkc2sud2lwZW1lYTpmcy5maWxlOnZmLlN1M29lUFVzUS1TajlaeVY1QkFwMXc_dmVyc2lvbj0z\",\"prev_forge_urn\":\"dXJuOmFkc2sud2lwZW1lYTpmcy5maWxlOnZmLlN1M29lUFVzUS1TajlaeVY1QkFwMXc_dmVyc2lvbj0y\"}"}
json
{"timestamp":"2026-06-24 23:07:04 KST","status":"error","message":"CPBimRevision::validateBimComparerResult | excessive removal detected - bimRevisionId: 24612, bimId: 19655, trigger: ratio 26.0% >= 15%"}
json
{"timestamp":"2026-06-24 23:07:04 KST","status":"error","message":"BimRevisionService::run | error: \"BimRevisionService::runBimCompare | end - validation failed (errorCode: REV141)\""}
json
{"timestamp":"2026-06-24 23:07:04 KST","status":"info","message":"BimRevisionService::REVISE-END | {\"bim_id\":19655,\"src_revision_id\":24612,\"prev_revision_id\":23776,\"si_trace_id\":\"bcddea93-45fb-4478-93b0-fccc9d6b0c7f\",\"elapsed_ms\":19388,\"status\":\"error\",\"error_message\":\"BimRevisionService::runBimCompare | end - validation failed (errorCode: REV141)\",\"counts\":{\"total\":0,\"modified\":0,\"removed\":0,\"exist\":0},\"cycle_state_changes\":{},\"model_id_changes\":{},\"external_id_changed_count\":0,\"bounds_changed_count\":0,\"anomaly_count\":0}"}

REVISE-ENDcounts.removed: 0 은 “REV141 발동으로 가드가 먼저 빠져나갔기 때문에 적용 단계까지 가지 않았다”는 뜻이다 — validateBimComparerResulthasExcessiveRemoval 분기에서 즉시 false 를 리턴해 setRemoved() 등 적용 루프를 실행하지 않는다 (cpbim-revision.ts:177-179).

14일 창에서 동일 메시지가 다른 BIM 에서도 반복 발생:

text
2026-06-18 22:42 KST  bim_id 17446  rev 24299 (vs 22131)  REV141
2026-06-18 19:32 KST  bim_id 17446  rev 24282 (vs 22131)  REV141
2026-06-18 12:52 KST  bim_id 2760   rev 3554  (vs 3112)   REV141
2026-06-18 10:58 KST  bim_id 2760   rev 3552  (vs 3112)   REV141
2026-06-18 10:33 KST  bim_id 2760   rev 3551  (vs 3112)   REV141

(bim_id: 17446, bim_id: 2760 은 동일 prev_revision 으로 여러 번 재시도된 흔적 — facility 가 임계값에 걸리는 변경이 들어오자 사용자가 같은 비교를 반복적으로 실행한 것으로 보임.)

기본 ratio threshold 는 40 % 이지만 (config/constants.ts:47 DefaultBimRevisionRemovedRatioThreshold = 40) 이번 facility (drdm2z) 는 entity parameter bim_revision_removed_ratio_threshold15 로 override 되어 있어 트리거 비교 메시지가 15% 로 출력된 것이다 (bim-revision-service.ts:765).

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Forge 비교에서 보고된 제거 비율이 facility 임계값(15%) 을 넘어 hasExcessiveRemoval 가드가 의도적으로 검증 실패시키고 REV141 을 throw 함 (안전 정지) excessive removal detected ... trigger: ratio 26.0% >= 15% 로그 (2026-06-24 23:07:04 KST). 코드 경로 cpbim-revision.ts:243removedRatio >= threshold.ratioThreshold 에서 true 반환 → validateBimComparerResultfalse 반환 → bim-revision-service.ts:687-690 에서 validationErrorCode: REV141 로 throw. REVISE-END.counts.removed: 0 (적용 단계 미진입) 일치. Confirmed
H2 Forge 자식 프로세스가 잘못된 응답을 반환해 runBimCompareBimRevisionForgeInvalidResponse (AGT1908) 로 실패한 케이스 bim-revision-service.ts:674-678 에 해당 분기 존재 실제 로그 메시지가 forge response is invalid 가 아니라 validation failed (errorCode: REV141) 이며, excessive removal detected 가드가 명시적으로 발동된 흔적이 있음 Rejected
H3 Forge 자체의 critical 에러 (ForgeErrorMapper.mapForgeErrorToRevisionError) 가 매핑되어 throw 된 케이스 bim-revision-service.ts:663-672 에 분기 존재. 비슷한 throw 메시지 형태 가능. forgeErrorCode: %s warn 로그 흔적 없음. 실제 throw 메시지는 Forge error: ... 이 아니라 validation failed (errorCode: REV141) 형태 Rejected
H4 단순 transient 네트워크/Tesla API 실패로 인해 loadBimRevision 등 초기 단계에서 throw REVISE-BEGIN 가 정상 출력 (revision 메타와 prev URN 모두 채워짐), cp_elements_created_count: 77. 즉 element/level 로드는 성공했고 forge 비교까지 도달함 Rejected
H5 임계값(15%) 이 facility drdm2z 의 실제 변경 패턴 대비 너무 보수적이라 정상적 리비전이 반복적으로 차단되는 chronic 문제 14일 창에서 같은 prev_revision(22131, 3112) 으로 여러 번 재시도된 흔적 (bim_id 17446, 2760). 동일 BIM 에서 REV141 반복. 단일 데이터 포인트만으로 “보수적” 단정 불가 — 비즈니스/안전 정책 결정 영역 Inconclusive — 비즈니스 결정 필요

Fix Recommendation#

즉시 조치 (Critical)#

코드 변경 불필요. 본 에러는 bim-revision-service.ts:687-690 의 의도적인 안전 정지(safety abort) 가 정상 동작한 결과이며, cpbim-revision.ts:234-265hasExcessiveRemoval 가드는 의도적으로 설계된 데이터 보호 장치다. 운영 측 조치만 필요:

  1. 데이터 검토: bimRevisionId: 24612 (bim_id: 19655, facility drdm2z, V1 → V2) 가 실제로 26% element 가 제거되어야 하는 의도된 변경인지 facility 담당자(exyte 팀) 확인.
    • 의도된 변경 → facility entity parameter bim_revision_bypass_threshold 를 일시적으로 true 로 설정 후 재실행 (bim-revision-service.ts:767-775).
    • 의도되지 않은 변경 → 사용자가 V2 forge URN 을 수정/재업로드 후 새 revision 으로 재시도.

단기 개선 (1주 이내)#

  1. 로그 레벨 재고: REV141 은 “정책 위반에 의한 안전 정지” 라서 error-sweeper 에 cluster 로 잡혀 매번 RCA 가 돌게 된다 (운영 부담). bim-revision-service.ts:189 의 catch 에서 ec.message 가 REV141 같이 “안전 정지 계열 errorCode” 일 때는 logger.warn 으로 격하하는 것을 검토. 코드 결함이 아닌 정책 발동을 cluster type=error 로 끌어올리는 것은 신호/잡음 비를 떨어뜨린다 (memory: AUTH20022/AUTH20023 다운그레이드 episode 와 동일 패턴).

  2. 에러 메시지에 trigger 정보 포함: 현재 throw 메시지는 validation failed (errorCode: REV141) 까지만 알려주고 “비율 26.0% vs 임계값 15%” 정보는 별도 로그에만 존재한다. throw message 에 trigger 컨텍스트(ratio 26.0% >= 15% 등) 를 포함시키면 cluster representative log 만 봐도 즉시 진단 가능. 변경 위치는 cpbim-revision.ts:260 부근에서 trigger 문자열을 인스턴스 필드로 보존, bim-revision-service.ts:687-690 에서 throw 시 합치는 형태.

  3. 모니터링 수치 분리: REV141 (정책 발동) vs AGT1908/AGT1904/AGT1905 (실제 에러) 를 Datadog 대시보드에서 다른 패널로 분리하여 운영 알림이 “정책 발동” 으로 깨지지 않도록.

장기 개선 (재발 방지)#

  1. Facility 별 임계값 운영 가이드: facility drdm2z 처럼 ratio threshold 를 15 로 낮춘 facility 가 14일 창에서 반복 REV141 을 일으키는지 정기적으로 점검. 너무 낮으면 사용자 워크플로 차단, 너무 높으면 안전 장치 무력화. 임계값 조정 책임 주체와 변경 이력을 entity parameter audit 으로 추적.
  2. 사용자 노출 메시지 정합성: bim_comparison_state: Error 로 기록되었을 때 사용자(facility 운영자) UI 가 “과도한 제거가 감지되어 안전 차단됨, 차이를 검토 후 bypass 또는 수정” 같이 actionable 한 사유를 보여주는지 확인. 단순 "Error" 로만 노출되면 사용자는 시스템 결함으로 오해.

Monitoring#

  • REV141 발동 추이 (정책 발동, 비율 표기)

    text
    sum:trace.express.request.errors\{service:cupixworks-any-bimrevision-agent,error.message:*REV141*\}.as_count()
    
  • REV141 facility 별 빈도 (excessive removal 가드 발동) — 로그 facet 기반:

    text
    logs("service:cupixworks-any-bimrevision-agent \"excessive removal detected\"").index("*").rollup("count").by("@bimId").last("1d")
    
  • BIM 리비전 비교 실패율 (모든 errorCode 포함, 단일 line)

    text
    logs("service:cupixworks-any-bimrevision-agent \"REVISE-END\" \"\\\"status\\\":\\\"error\\\"\"").index("*").rollup("count").last("1d")
    
  • 알람 추천: facility 단일 키(@bimId 혹은 @facilityKey) 에서 동일 prev_revision 으로 24h 내 REV141 가 3회 이상 → 임계값 조정 또는 사용자 가이드 필요 신호.

Risk Assessment#

  • Risk level: low — 코드 결함이 아니라 의도된 안전 정지의 정상 동작. 데이터 손상이나 다운스트림 장애 없음.
  • 예상 복잡도: trivial — 코드 변경 자체는 즉시 불필요. 단기 개선 2번(throw 메시지에 trigger 포함) 은 5-10줄 변경, throw site 두 곳 동기화만 하면 됨. 운영/임계값 결정은 비즈니스 결정으로 분리 추적.