[Integration] failed to refresh token for bim360 integration(239) - state: failed, error_message: BI
RCA: [Integration] failed to refresh token for bim360 integration(239)
Error Log#
[Integration] failed to refresh token for bim360 integration(239) - state: failed, error_message: BIM360 Authentication failed:
Impact#
- Service:
cupixworks-worker - 발생 횟수: 1
- 최초 발생: 2026-04-13T16:07:22.677Z
- 최근 발생: 2026-04-13T16:07:22.677Z
Root Cause Summary#
BIM360 integration(239)의 OAuth access token이 2026-03-31에 만료된 후 약 14일간 갱신되지 않은 상태에서, cron job이 refresh token을 사용해 갱신을 시도했으나 Autodesk OAuth 서버가 500 Internal Server Error를 반환했다. Autodesk 측 서버 오류로 인해 Bim360Operation.refresh_token에서 RestClient::Exception이 발생했고, 응답의 developerMessage가 비어 있어 에러 메시지가 "BIM360 Authentication failed:" (콜론 뒤 빈 값)로 기록되었다. 이후 integration 상태가 failed로 전환되고 refresh_token_expired_at이 nil로 설정되어, 향후 cron job에서 자동 재시도 대상에서 제외되었다.
Technical Analysis#
Code Path#
- Entry point:
config/schedule.rb:94— 4시간마다:07분에 실행되는 cron job - Cron handler:
lib/cupix/cron/integration.rb:3-15—renew_before_expiration메서드가 갱신 대상 integration을 조회하여 순차적으로 토큰 갱신 시도
# lib/cupix/cron/integration.rb:3-15
def self.renew_before_expiration
candidates = Integration.refresh_token_due_to_expire
return nil if candidates.blank?
candidates.each do |model|
integration_repository = IntegrationRepository.new(model)
integration_repository.refresh_token
rescue StandardError => e
Cupix::Logger.error("[Cupix::Cron::Integration] failed to renew integration: #{e.message}")
next
end
end
- Scope 조건:
app/models/concerns/statable/integration.rb:12—not_failed상태이면서refresh_token_expired_at이 1일 이내인 integration을 조회
# app/models/concerns/statable/integration.rb:12
scope :refresh_token_due_to_expire, -> {
not_failed.where.not(refresh_token_expired_at: nil)
.where('refresh_token_expired_at < ?', 1.days.since)
}
Integration 239는 inactive 상태, refresh_token_expired_at: 2026-04-14 12:07:17 UTC으로 scope 조건에 부합했다.
- Token refresh 실행:
app/repositories/integration_repository.rb:103-170— provider에 따라Bim360Operation을 선택하고refresh_token호출
# app/repositories/integration_repository.rb:106-128
Cupix::Logger.info("[Integration] refresh token for #{@model.provider} integration(#{@model.id}) - ...")
operation_class = case @model.provider
when 'bim360'
Bim360Operation
# ...
end
begin
unless check_refresh_request
raise Cupix::Errors::Parameter.new(code: 'ARG10060', reason: 'Refresh access token request is too many')
end
token = operation_class.refresh_token(@model.refresh_token, @model.region)
- Failure point:
app/operations/bim360_operation.rb:60-70— Autodesk OAuth 서버에RestClient.post요청 시500 Internal Server Error발생
# app/operations/bim360_operation.rb:60-70
begin
url = "#{$OAUTH[:autodesk_forge][:site]}#{$OAUTH[:autodesk_forge][:refresh_url]}"
response = RestClient.post(url, data, header)
rescue RestClient::Exception => e
response = JSON.parse(e.response)
Cupix::Logger.error("BIM360 refresh_token failed: #{response['developerMessage']} - error: #{e.message}")
raise Cupix::Errors::Parameter.new(
code: 'ARG10000',
reason: "BIM360 Authentication failed: #{response['developerMessage']}"
)
end
RestClient::Exception이 캐치되었고, Autodesk 응답에 developerMessage가 없거나 빈 값이어서 "BIM360 Authentication failed:" (빈 메시지)가 raise되었다.
- Error handling (상태 전환):
app/repositories/integration_repository.rb:130-142— 예외 캐치 후 integration을failed상태로 전환
# app/repositories/integration_repository.rb:130-142
rescue StandardError => e
uncheck_refresh_request
raise e if e.code == 'ARG10060'
@model.refresh_token_failed_at = DateTime.now
@model.refresh_token_expired_at = nil # cron 재시도 대상에서 제외
@model.refresh_token_response_body = token # token은 nil (할당 전에 예외 발생)
@model.failed_state! # state → 'failed'
Cupix::Logger.error("[Integration] failed to refresh token for #{@model.provider} integration(#{@model.id}) - state: #{@model.state}, error_message: #{e.message}")
raise e
end
refresh_token_expired_at이 nil로 설정되므로, refresh_token_due_to_expire scope 조건(where.not(refresh_token_expired_at: nil))에서 제외되어 자동 재시도가 불가능해진다.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-worker "bim360" "239"
Time range: 2026-04-13T14:00:00Z to 2026-04-13T17:30:00Z
service:cupixworks-worker "BIM360 refresh_token failed"
Time range: 2026-04-13T15:00:00Z to 2026-04-13T17:30:00Z
이벤트 타임라인 (모두 2026-04-14 01:07:22 KST = 2026-04-13T16:07:22Z):
- [INFO] Cron job이 integration 239를 갱신 대상으로 감지:
[Integration] refresh token for bim360 integration(239) - state: inactive, expired_at: 2026-03-31 13:21:16 UTC, refresh_token_expired_at: 2026-04-14 12:07:17 UTC
- [ERROR] Autodesk OAuth 서버가 500 에러를 반환 (
developerMessage비어 있음):
BIM360 refresh_token failed: - error: 500 Internal Server Error
- [ERROR] IntegrationRepository에서
failed상태로 전환 후 에러 로깅:
[Integration] failed to refresh token for bim360 integration(239) - state: failed, error_message: BIM360 Authentication failed:
- [ERROR] Cron handler에서 예외를 캐치하고 다음 integration으로 진행:
[Cupix::Cron::Integration] failed to renew integration: BIM360 Authentication failed:
핵심 관찰 사항:
expired_at: 2026-03-31— access token이 약 14일 전에 이미 만료됨refresh_token_expired_at: 2026-04-14 12:07:17 UTC— refresh token 만료까지 약 20시간 남은 시점- Autodesk 서버가
500 Internal Server Error를 반환 — 클라이언트 측 오류(401, 400 등)가 아닌 서버 측 일시적 장애 developerMessage가 비어 있어 구체적인 실패 원인을 Autodesk 측에서 제공하지 않음
Fix Recommendation#
즉시 조치 (Critical)#
- 수동 재인증 필요: Integration 239가
failed상태이므로 cron job에서 자동 재시도되지 않는다. 사용자가 BIM360 OAuth 플로우를 다시 수행하여 새 token을 발급받아야 한다. - 해당 integration을 사용 중인 사용자/팀에 연락하여 재연결을 안내해야 한다.
단기 개선 (1주 이내)#
- 일시적 장애에 대한 재시도 로직 추가:
app/operations/bim360_operation.rb:63—RestClient::Exception에서 HTTP 5xx 응답인 경우 즉시failed상태로 전환하지 않고, 지수 백오프(exponential backoff)로 재시도하는 로직이 필요하다. 현재는 서버 측 일시적 장애(500)도 클라이언트 오류(400, 401)와 동일하게 처리되어 즉시failed상태로 빠진다. refresh_token_expired_at보존:app/repositories/integration_repository.rb:136— 5xx 에러 시refresh_token_expired_at을nil로 설정하지 않아야 한다.nil로 설정하면 cron job 재시도 대상에서 영구 제외되므로, 서버 측 일시적 장애에서 자동 복구가 불가능해진다.
장기 개선 (재발 방지)#
- HTTP 상태 코드 기반 에러 분류: 4xx (클라이언트 오류) vs 5xx (서버 오류)를 구분하여 처리 전략을 달리해야 한다. 5xx는 재시도 가능한 일시적 장애로, 4xx (특히 401)는 재인증이 필요한 영구적 실패로 분류하는 것이 적절하다.
- Integration 상태에 재시도 카운트 추가: 연속 실패 횟수를 추적하여, N회 이상 실패 시에만
failed상태로 전환하는 방식이 보다 견고하다. - 알림 시스템: Integration이
failed상태로 전환될 때 해당 사용자/팀에 자동 알림을 보내어 빠른 재인증을 유도해야 한다.
Monitoring#
failed상태 integration 모니터링:
service:cupixworks-worker "[Integration] failed to refresh token" status:error
- BIM360 서버 측 오류 빈도 추적:
service:cupixworks-worker "BIM360 refresh_token failed" "500"
- 장기 미갱신 integration 탐지를 위한 메트릭:
count:integration.refresh_token.failure{provider:bim360} by {status_code}
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard — Autodesk 서버 측 일시적 장애로 인한 단일 integration 실패. 현재 영향 범위는 integration 239 하나에 한정되나, 동일 패턴이 다른 BIM360 integration에도 발생할 수 있다. 재시도 로직 부재로 인해 일시적 장애가 영구적 실패로 확대되는 구조적 문제가 존재한다.