ES /docs

fail to translation bim. BimRevision: 5194 Status: failed, Messages: [{"type"=>"error", "code"=>"Rev

RCA: fail to translation bim — Revit-UnsupportedFileType

Overview#

What Happened#

2026-07-13 16:44 KST에 cupixworks-workerBimRevisionRepository#finish_forge_translating! 가 Autodesk Forge manifest 조회 결과 status: failed 를 받고 Cupix::Logger.error 로 로그를 기록했다. Forge 측 실패 사유는 Revit-UnsupportedFileType — 사용자가 업로드한 파일이 Revit 파일이 아니거나 지원하지 않는 버전이라는 뜻이다. 시스템 자체의 예외/스택트레이스는 없으며, 외부 시스템(Forge) 의 사용자 입력 검증 실패를 서비스가 그대로 error 레벨로 흘려보내고 있다.

Quick Facts#

Field Value
exception.class - (예외 아님, 로그 메시지)
exception.message fail to translation bim. BimRevision: 5194 Status: failed, Messages: [...Revit-UnsupportedFileType...]
top_frame app/repositories/bim_revision_repository.rb:82
logger Cupix::Logger.error (class=BimRevisionRepository, function=finish_forge_translating!)
env production, ap-southeast-2, tenant=cupix

Affected Teams#

Team / Domain Error Count Impact
BIM / Forge integration (cupixworks-worker) 1 (해당 클러스터) / 지난 14일간 다수 (Revit-UnsupportedFileType 로 20+건 반복) 개별 사용자의 BimRevision 하나가 error_forge_state 로 전이됨. 다른 사용자/기능 영향 없음. 지속적 error 로그로 인해 모니터링 noise 발생.

Timeline#

  1. 2026-07-13 16:44:44 KST — Autodesk Forge webhook 이 forge_translation_finished_callback 을 호출, FinishBimRevisionForgeTranslationWorker 가 enqueue.
  2. 2026-07-13 16:44:44 KST — Worker 가 Forge manifest 를 조회하고 status=failed, code=Revit-UnsupportedFileType 를 수신, error_forge_state 로 전이 후 Cupix::Logger.error 기록 (Datadog cluster 8a242739).
  3. 참고 — 동일 패턴이 2026-07-07 ~ 2026-07-13 사이 반복 발생 (BimRevision 3820, 5174, 5194, 6415~6429 등), status-board 상 active svc incident 2026-07-13-svc-cupixworks-worker--unknown-2 의 구성 클러스터 중 하나.

Error Log#

Datadog Logs

text
fail to translation bim. BimRevision: 5194 Status: failed, Messages: [{"type"=>"error", "code"=>"Revit-UnsupportedFileType", "message"=>"<message>The file is not a Revit file or is not a supported version.</message>"}, {"type"=>"error", "message"=>"Possibly recoverable warning exit code from extractor: -536870935", "code"=>"TranslationWorker-RecoverableInternalFailure"}]

Impact#

  • Service: cupixworks-worker
  • 발생 횟수: 1 (이 클러스터), 유사 Revit-UnsupportedFileType 로그는 최근 14일 20+건
  • 최초 발생: 2026-07-13 16:44 KST
  • 최근 발생: 2026-07-13 16:44 KST
  • Blast radius: BimRevision 단일 레코드(5194). 사용자에게는 UI 에서 "번역 실패" 상태로 표시됨. 시스템 안정성/처리량 영향 없음.

Root Cause Summary#

Autodesk Forge Model Derivative API 가 사용자 업로드 파일에 대해 status: failed, code: Revit-UnsupportedFileType 를 반환했고, BimRevisionRepository#finish_forge_translating! 는 이 정상적(=예상 가능한) 외부 실패 응답을 Cupix::Logger.error 로 기록하도록 구현되어 있다. 즉 코드 결함이 아니라 사용자 입력 오류(비 Revit 파일 또는 미지원 버전) 를 error 레벨로 승격시키는 로깅 정책 이 원인이다. 워커는 이미 error_forge_state 로 상태를 전이시키며 예외를 던지지 않으므로 Sidekiq 재시도나 데이터 손상은 발생하지 않는다.

Technical Analysis#

Code Path#

  • Entry point (webhook): app/controllers/api/v1/bim_revisions_controller.rb:32forge_translation_finished_callbackFinishBimRevisionForgeTranslationWorker.perform_async(@model.id) 를 enqueue.
  • Worker: app/workers/finish_bim_revision_forge_translation_worker.rb:5 — BimRevision 을 조회 후 finish_forge_translating! 호출.
  • Failure point: app/repositories/bim_revision_repository.rb:79-82 — Forge manifest 응답 status'failed' 인 branch 에서 error_forge_state 전이 후 error 로그.
app/controllers/api/v1/bim_revisions_controller.rb:32-44ruby
def forge_translation_finished_callback
  @model = ::BimRevision.find_by(forge_state: :translating, id: params[:id])

  raise Cupix::Errors::NotFound.new(code: 'ARG10002', reason: 'BimRevision not found') if @model.blank?

  FinishBimRevisionForgeTranslationWorker.perform_async(@model.id)

  render_api
rescue Cupix::Errors::NotFound => e
  raise e
rescue StandardError => e
  raise Cupix::Errors::BadGateway.new(code: 'BG10001', reason: e.to_s)
end
app/workers/finish_bim_revision_forge_translation_worker.rb:1-14ruby
class FinishBimRevisionForgeTranslationWorker
  include Sidekiq::Worker
  sidekiq_options queue: :default, retry: 1

  def perform(bim_revision_id)
    bim_revision = ::BimRevision.find_by(id: bim_revision_id)

    return if bim_revision.blank?

    bim_revision_repository = BimRevisionRepository.new(model: bim_revision)

    bim_revision_repository.finish_forge_translating!
  end
end
app/repositories/bim_revision_repository.rb:70-85ruby
# Overall status of the translation jobs recorded in the "manifest". Possible values: pending, success, inprogress, failed, timeout
case _json_response['status']
when 'success'
  if _svf == 'svf'
    # if svf, another forge-agent will be launched and update to translated forge state
    self.model.complete_forge_translate!
  else
    self.model.translated_forge_state
  end
when 'failed', 'timeout'
  self.model.error_forge_state
  _messages = _bim_derivative['messages'] rescue nil
  Cupix::Logger.error("fail to translation bim. BimRevision: #{self.model.id} Status: #{_json_response['status']}, Messages: #{_messages}", class: self.class.name, function: __method__, forge_response: _json_response)
else
  Cupix::Logger.info("translation is done but status is not 'success' or 'failed'. BimRevision: #{self.model.id} Undefined Status: #{_json_response['status']}", class: self.class.name, function: __method__, forge_response: _json_response)
end

기대 동작: Forge 가 failed 를 리턴할 때는 (a) 사용자 입력 오류 (Revit-UnsupportedFileType, Revit-ProtectedFile, Revit-CorruptedFilecode 접두어가 Revit- 인 항목), (b) Forge 내부 일시 오류 (TranslationWorker-*), (c) 실제 시스템 문제로 구분되어야 하며, (a) 는 warn 이하 레벨로 낮추어 error 대시보드/알림에 노이즈를 남기지 않아야 함.

실제 동작: 모든 failed / timeout 응답을 일괄 Cupix::Logger.error 로 기록. Forge 응답 메시지에 실패 코드가 담겨 있음에도 코드별 분기 없음.

Log Evidence#

Datadog query (재현용):

text
service:cupixworks-worker "fail to translation bim"

이 클러스터의 원본 로그:

json
{
  "timestamp": "2026-07-13 16:44:44",
  "status": "error",
  "message": "fail to translation bim. BimRevision: 5194 Status: failed, Messages: [{\"type\"=>\"error\", \"code\"=>\"Revit-UnsupportedFileType\", \"message\"=>\"<message>The file is not a Revit file or is not a supported version.</message>\"}, {\"type\"=>\"error\", \"message\"=>\"Possibly recoverable warning exit code from extractor: -536870935\", \"code\"=>\"TranslationWorker-RecoverableInternalFailure\"}]",
  "class": "BimRevisionRepository",
  "function": "finish_forge_translating!"
}

동일 코드 경로에서 반복 발생하는 최근 14일간의 표본 (query: service:cupixworks-worker "Revit-UnsupportedFileType"):

text
2026-07-13 16:44:44  BimRevision: 5194  Revit-UnsupportedFileType
2026-07-10 16:41:18  BimRevision: 5174  Revit-UnsupportedFileType
2026-07-10 14:45:36  BimRevision: 3820  Revit-UnsupportedFileType
2026-07-07 13:21:xx  BimRevision: 6415~6429 (10+건)  Revit-UnsupportedFileType
  • 모든 로그가 동일한 class=BimRevisionRepository, function=finish_forge_translating! 태그를 가짐 → 코드 경로 확정.
  • 실패 원인이 항상 사용자 입력(비 Revit 파일)이며, 스택트레이스가 없음 → 애플리케이션 예외 아님.
  • 워커는 예외를 raise 하지 않으므로 Sidekiq 재시도가 발생하지 않고 job 은 성공으로 종료 — 예외 트래킹 도구가 아니라 log stream 에만 error 로 남는다.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Forge 가 사용자 업로드 파일을 Revit-UnsupportedFileType 으로 거절한 것을 서비스가 error 레벨로 로깅하는 것이 근본 원인 (사용자 입력 오류를 error 로 승격) Datadog 로그 메시지 원문에 code=Revit-UnsupportedFileType, message=The file is not a Revit file or is not a supported version 명시. bim_revision_repository.rb:79-82 에서 failed branch 가 무조건 Logger.error 호출. 동일 패턴이 14일간 20+건 반복. 없음 Confirmed
H2 Autodesk Forge 서비스 장애 (외부 dependency outage) 동일 시간대 동일 서비스에서 다른 종류의 Forge 에러 다수 발생했을 가능성 로그 메시지가 명시적으로 사용자 파일 형식 문제를 지목. TranslationWorker-RecoverableInternalFailure 는 부수 코드이나 Forge 는 Revit-UnsupportedFileType 를 primary error 로 반환. Forge status page 확인 필요하나, 사용자 입력 오류가 명시된 이상 dependency outage 로 볼 근거 없음. status-board 상 dep:* scope 아님 (svc scope). Rejected
H3 Sidekiq worker retry / race condition 으로 이미 처리된 BimRevision 을 재처리 finish_forge_translating!return unless self.model.forge_state_translating? 로 guard (bim_revision_repository.rb:45). retry: 1 설정. 로그가 1건만 발생, guard 문에 의해 재실행되어도 no-op. 여러 revision id 가 서로 다르므로 재시도 아님. Rejected
H4 Forge 인증(access_token) / manifest 조회 실패로 인한 서비스 결함 HTTP 호출 라인이 있음 (bim_revision_repository.rb:47-64) manifest JSON 을 파싱하여 status, derivatives, messages 필드까지 접근에 성공. 즉 인증/네트워크 정상, Forge 는 정상적으로 실패 응답을 반환한 것. Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 파일: app/repositories/bim_revision_repository.rb:79-82
  • 접근 방식: _bim_derivative['messages'] 에서 code 를 검사하여, Revit- 접두어(사용자 파일 문제) 또는 Revit-UnsupportedFileType / Revit-ProtectedFile / Revit-CorruptedFile 등 사용자 입력 계열은 Cupix::Logger.warn 으로, 그 외 (TranslationWorker-* 중 fatal, 인증 관련, 알 수 없는 상태) 는 기존대로 Cupix::Logger.error 유지.
  • 근거: Revit-UnsupportedFileType 은 정의상 사용자 업로드가 잘못된 경우이며, 이를 error 로 유지하면 대시보드/알림이 사용자 파일 오류로 오염된다. Memory 의 "RCA Quality Patterns — Assess error severity" 가이드와 일치.
  • 주의: 사용자 상태 전이(error_forge_state) 는 유지해야 UI 에서 "번역 실패" 로 표시된다. 로그 레벨만 낮춘다.

단기 개선 (1주 이내)#

  • Forge 응답 코드 분류 helper 추가app/repositories/bim_revision_repository.rb 인접 (혹은 lib/cupix/forge/ 하위) 에 classify_forge_failure(messages) -> :user_input | :forge_transient | :system 형태 유틸을 만들어 로그 분기 로직을 재사용 가능하게 한다. BimRepository#finish_forge_translating! (bim_repository.rb:63) 도 동일 로직이 있는지 확인 후 함께 적용.
  • BimRevision 실패 사유를 모델 필드에 저장 — 현재는 로그로만 실패 이유가 남으므로, 사용자가 UI 에서 "왜 실패했는지" 알 수 없다. error_forge_state 전이 시 _messagesbim_revision.forge_error_code 등 컬럼에 저장하여 사용자 안내 문구를 개선 (별도 스토리로 프런트/PM 협의 필요 — 자동화 fix 대상 아님).

장기 개선 (재발 방지)#

  • Error taxonomy 문서화 — 외부 dependency 응답을 error 로 로그할지 warn 으로 로그할지에 대한 팀 가이드라인 (예: "사용자 입력 유효성 실패는 warn, 외부 서비스 5xx/timeout 은 error"). 반복적으로 RCA 후에 log-level 을 downgrade 하는 episode 가 축적되고 있음.
  • Datadog Error Tracking 필터Revit-UnsupportedFileType 등 사용자 입력 계열 실패는 Error Tracking 대시보드에서 제외되도록 tag/filter 규칙을 설정. 로그 레벨 변경과 병행하면 앞으로 클러스터 자체가 생성되지 않는다.

Monitoring#

Datadog release dashboard widget 용 timeseries 쿼리:

text
logs("service:cupixworks-worker @class:BimRevisionRepository @function:finish_forge_translating! status:error").index("*").rollup("count").by("@forge_response.derivatives.messages.code")
  • 위 쿼리는 실패 코드별 시계열을 보여주어 Revit-UnsupportedFileType 비중과 그 외 실제 시스템 에러를 분리 가시화한다.
  • 픽스 배포 후 검증: Revit-* 코드는 error → warn 으로 이동하고, 잔여 error 로그는 실제 시스템 에러여야 함.

부가 검증 쿼리 (레벨 이동 확인):

text
logs("service:cupixworks-worker @class:BimRevisionRepository @function:finish_forge_translating! status:warn \"Revit-UnsupportedFileType\"").index("*").rollup("count")

Risk Assessment#

  • Risk level: low — 로그 레벨 변경 위주, 비즈니스 로직/상태 전이 미변경.
  • 예상 복잡도: trivial — 단일 파일, 조건부 로거 호출 변경. 유사 지점(BimRepository) 도 함께 검토 필요하나 동일 패턴이면 소규모 diff.
  • 반영 시 주의: _bim_derivative['messages'] 는 배열이며 code 가 없거나 형태가 다를 수 있으므로 Array(_messages).any? { |m| m['code'].to_s.start_with?('Revit-') } 형태의 안전한 접근 필요. Sorbet/typecheck 없이 Ruby 관례로 처리.