BIM360 refresh_token failed: - error: 500 Internal Server Error
RCA: BIM360 refresh_token failed: - error: 500 Internal Server Error
Error Log#
BIM360 refresh_token failed: - error: 500 Internal Server Error
Impact#
- Service:
cupixworks-worker - 발생 횟수: 1
- 최초 발생: 2026-04-13T16:07:22.677Z
- 최근 발생: 2026-04-13T16:07:22.677Z
Root Cause Summary#
BIM360(Autodesk) OAuth token refresh 크론 작업(Cupix::Cron::Integration.renew_before_expiration)이 4시간마다 실행되면서 만료 예정인 integration의 refresh token을 갱신하는 과정에서, eu-central-1 리전의 integration ID 239에 대해 Autodesk Forge OAuth API가 HTTP 500 Internal Server Error를 반환했습니다. 이는 Autodesk 서버 측의 일시적 장애로, 해당 시점에 refresh token은 아직 유효한 상태였습니다(refresh_token_expired_at: 2026-04-14 12:07:17 UTC, 에러 발생 약 20시간 전). 현재 코드에는 일시적 외부 API 실패에 대한 재시도 로직이 없어, 단 한 번의 500 응답으로 integration이 failed 상태로 전환되었습니다.
Technical Analysis#
Code Path#
- Entry point:
config/schedule.rb:94— 4시간마다(7 */4 * * *)Cupix::Cron::Integration.renew_before_expiration실행
# config/schedule.rb:91-94
every '7 */4 * * *' do # 00:07,04:07,08:07 ...
runner 'Cupix::Cron::Integration.renew_before_expiration'
end
- Cron handler:
lib/cupix/cron/integration.rb:3-16— 만료 예정인 integration을 조회하여 순차적으로 refresh
# lib/cupix/cron/integration.rb:3-16
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 # 실패해도 다른 integration은 계속 처리
end
end
- Scope 조건:
app/models/concerns/statable/integration.rb:12—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)
}
- Repository 실행:
app/repositories/integration_repository.rb:103-142— provider별 operation class를 선택하고 token refresh를 실행
# app/repositories/integration_repository.rb:123-142
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)
uncheck_refresh_request
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 # 만료 시각 초기화
@model.refresh_token_response_body = token
@model.failed_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
- Failure point:
app/operations/bim360_operation.rb:60-70— RestClient가 Autodesk API에 POST 요청 시 500 응답을 받아 예외 발생
# 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
기대 동작: Autodesk API가 새 access_token과 refresh_token을 JSON으로 반환하고, integration의 토큰이 갱신됨.
실제 동작: Autodesk API가 HTTP 500을 반환 → RestClient::Exception 발생 → Cupix::Errors::Parameter raise → IntegrationRepository에서 catch하여 integration 239를 failed 상태로 전환 → refresh_token_expired_at을 nil로 설정. 이후 크론 작업에서는 not_failed scope으로 인해 이 integration이 자동 갱신 대상에서 제외됨.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-worker status:error "BIM360 refresh_token failed"
service:cupixworks-worker status:error "BIM360"
service:cupixworks-worker "BIM360"
에러 로그 타임라인 (eu-central-1, 2026-04-13T16:07:22.677Z):
1단계 — Autodesk API 500 응답:
BIM360 refresh_token failed: - error: 500 Internal Server Error
Host: ip-10-1-80-125.eu-central-1.compute.internal, PID: 3049253
2단계 — Integration 상태 전환:
[Integration] failed to refresh token for bim360 integration(239) - state: failed, error_message: BIM360 Authentication failed:
3단계 — Cron 레벨 에러 보고:
[Cupix::Cron::Integration] failed to renew integration: BIM360 Authentication failed:
Integration 239의 refresh 전 상태 (info 로그):
[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
refresh_token_expired_at이2026-04-14 12:07:17 UTC로 에러 시점에서 약 20시간 남아 있어, refresh token 자체의 만료가 아닌 Autodesk 서버 측 일시적 오류임을 확인.state: inactive이며expired_at: 2026-03-31— access token은 이미 만료되었으나 refresh token은 유효한 상태.
대조: 동일 시간대 us-west-2 리전에서는 48개 이상의 BIM360 integration이 정상 갱신됨 — 문제가 Autodesk EU 리전 서버에 국한된 일시적 장애임을 시사.
Fix Recommendation#
즉시 조치 (Critical)#
app/operations/bim360_operation.rb:60-62:RestClient.post호출에 재시도 로직 추가. HTTP 500, 502, 503, 504 등 서버 오류에 대해 지수 백오프(exponential backoff)로 최대 3회 재시도하도록 변경. 일시적 Autodesk 서버 장애로 인해 integration이 영구failed상태로 전환되는 것을 방지.
단기 개선 (1주 이내)#
-
app/repositories/integration_repository.rb:135-138: 일시적 외부 API 오류(5xx)와 영구적 인증 오류(401, 403)를 구분하여, 5xx 에러의 경우 즉시failed상태로 전환하지 않고refresh_token_failed_at만 기록한 후 다음 크론 사이클에서 재시도할 수 있도록 변경. 현재는 단 한 번의 500 응답으로도failed_state!가 호출되어 복구가 불가능함. -
app/models/concerns/statable/integration.rb:12:refresh_token_due_to_expirescope에 최근 실패한 integration도 일정 횟수까지 재시도 대상에 포함하는 로직 고려. 현재not_failed조건으로 인해 한 번 실패하면 자동 갱신 대상에서 완전히 제외됨.
장기 개선 (재발 방지)#
- Integration의
failed상태에서 자동 복구 메커니즘 도입. 예: 연속 실패 횟수(retry_count)를 추적하여, 일정 횟수(예: 5회) 이상 연속 실패 시에만failed상태로 전환하고 관리자 알림 발송. - Autodesk API 응답 시간 및 에러율에 대한 외부 서비스 모니터링 추가.
Monitoring#
- BIM360 token refresh 실패를 감지하는 Datadog 모니터 추가:
service:cupixworks-worker "BIM360 refresh_token failed" status:error
- Integration
failed상태 전환 빈도 추적:
service:cupixworks-worker "[Integration] failed to refresh token" status:error
- 리전별 BIM360 refresh 성공/실패 비율 대시보드 구성을 통해 특정 리전의 Autodesk API 장애를 조기 감지.
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard
- 단일 integration(ID 239)에만 영향. eu-central-1 리전의 Autodesk 서버 일시적 장애로 판단되며, 동일 시간대 us-west-2의 모든 integration은 정상 갱신됨. 그러나 재시도 로직 부재로 인해 일시적 외부 오류에도 integration이 영구 실패 상태로 전환되는 구조적 취약점이 존재함.