[Cupix::Cron::Integration] failed to renew integration: BIM360 Authentication failed:
RCA: [Cupix::Cron::Integration] failed to renew integration: BIM360 Authentication failed:
Error Log#
[Cupix::Cron::Integration] failed to renew integration: BIM360 Authentication failed:
Impact#
- Service:
cupixworks-worker - 발생 횟수: 1
- 최초 발생: 2026-04-13T16:07:22.677Z
- 최근 발생: 2026-04-13T16:07:22.677Z
Root Cause Summary#
4시간 간격으로 실행되는 Cupix::Cron::Integration.renew_before_expiration 크론 작업이 eu-central-1 리전의 BIM360 integration(239)에 대해 OAuth refresh token을 갱신하려 했으나, Autodesk Forge OAuth API가 HTTP 500 Internal Server Error를 반환하여 실패했습니다. 해당 integration의 access token은 이미 13일 전(2026-03-31)에 만료된 상태였고, refresh token도 약 20시간 후(2026-04-14 12:07:17 UTC)에 만료 예정이었습니다. Autodesk 측 서버 오류로 인해 토큰 갱신이 불가능했으며, 실패 후 integration 상태가 failed로 전환되어 이후 크론 실행에서도 갱신 대상에서 제외됩니다. 동일 시간대 us-west-2 리전의 다른 BIM360 integration들은 모두 정상적으로 갱신되었으므로, 이 건은 Autodesk API의 일시적 오류에 의한 단건 장애입니다.
Technical Analysis#
Code Path#
- Entry point:
config/schedule.rb:91— 4시간 간격 크론 스케줄 (every '7 */4 * * *') - 크론 실행:
lib/cupix/cron/integration.rb:3—renew_before_expiration메서드가 갱신 대상 integration을 조회
# lib/cupix/cron/integration.rb:3-14
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
- 대상 선정:
app/models/concerns/statable/integration.rb:12—refresh_token_due_to_expirescope가not_failed상태이면서refresh_token_expired_at이 1일 이내인 integration을 조회
# app/models/concerns/statable/integration.rb:11-12
scope :not_failed, -> { where(state: %i[active inactive]) }
scope :refresh_token_due_to_expire, -> { not_failed.where.not(refresh_token_expired_at: nil).where('refresh_token_expired_at < ?', 1.days.since) }
- 토큰 갱신:
app/repositories/integration_repository.rb:103-128—IntegrationRepository#refresh_token이 provider별 operation 클래스를 선택하여 토큰 갱신 요청
# app/repositories/integration_repository.rb:106-128
Cupix::Logger.info("[Integration] refresh token for #{@model.provider} integration(#{@model.id}) - state: #{@model.state}, expired_at: #{@model.expired_at}, refresh_token_expired_at: #{@model.refresh_token_expired_at}")
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:62-70— Autodesk OAuth API 호출 시RestClient::Exception발생 (HTTP 500),Cupix::Errors::Parameter예외로 변환
# 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']}"
)
- 실패 후 처리:
app/repositories/integration_repository.rb:130-142— 예외 catch 후 integration 상태를failed로 전환하고refresh_token_expired_at을 nil로 설정
# 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
@model.refresh_token_response_body = token
@model.failed_state!
Cupix::Logger.error("[Integration] failed to refresh token for #{@model.provider} integration(#{@model.id}) - state: #{@model.state}, error_message: #{e.message}")
raise e
기대 동작: Autodesk Forge OAuth API가 새로운 access_token/refresh_token을 반환하고, integration이 갱신됨.
실제 동작: Autodesk API가 HTTP 500을 반환하여 토큰 갱신 실패. Integration 상태가 failed로 전환되어 이후 크론에서 갱신 대상에서 영구 제외됨.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-worker status:error "BIM360" @environment:production
service:cupixworks-worker "Cron::Integration" OR "BIM360" OR "renew" @environment:production
service:cupixworks-worker status:error "failed to renew integration"
핵심 로그 타임라인 (모두 2026-04-13T16:07:22.677Z, 동일 호스트 ip-10-1-80-125.eu-central-1.compute.internal, PID 3049253):
- [INFO] 갱신 시도 시작 — 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 API 호출 실패 — HTTP 500 응답:
BIM360 refresh_token failed: - error: 500 Internal Server Error
developerMessage가 비어 있음 (Autodesk 서버 내부 오류로 상세 메시지 미제공).
- [ERROR] Integration 상태 변경 —
failed상태로 전환:
[Integration] failed to refresh token for bim360 integration(239) - state: failed, error_message: BIM360 Authentication failed:
- [ERROR] 크론 작업 최종 에러 로그:
[Cupix::Cron::Integration] failed to renew integration: BIM360 Authentication failed:
비교: 같은 시간대 us-west-2 리전은 정상 갱신:
[Integration] Successfully refreshed token for bim360 integration(4877) - state: active
[Integration] Successfully refreshed token for bim360 integration(4742) - state: active
[Integration] Successfully refreshed token for bim360 integration(5817) - state: active
us-west-2에서는 수십 개의 BIM360 integration이 모두 정상적으로 갱신되었으며, eu-central-1의 integration(239)만 실패했습니다.
최근 7일간 동일 에러 검색 결과: 이 건이 유일한 "failed to renew integration" 에러입니다.
Fix Recommendation#
즉시 조치 (Critical)#
- integration(239) 수동 복구: 현재
failed상태로 전환되어 크론 갱신 대상에서 제외된 상태입니다. 사용자가 BIM360 연동을 다시 설정(재인증)하거나, DB에서 수동으로 상태를inactive로 복원하고 refresh token이 아직 유효한 경우 다음 크론 실행에서 갱신을 재시도할 수 있습니다. 다만 refresh_token_expired_at이 2026-04-14 12:07:17 UTC이므로 이미 만료되었을 가능성이 높아 재인증이 필요합니다.
단기 개선 (1주 이내)#
- 재시도 로직 추가 (
app/repositories/integration_repository.rb:130): 외부 API의 5xx 오류 시 즉시failed상태로 전환하지 않고, 재시도 카운터를 두어 N회 연속 실패 후에만failed로 전환하는 방식이 필요합니다. 현재는 Autodesk API의 일시적 500 에러 한 번으로 integration이 영구적으로 비활성화됩니다. - 에러 분류 개선 (
app/operations/bim360_operation.rb:63-70): 4xx 에러(인증 문제)와 5xx 에러(서버 오류)를 구분하여, 5xx의 경우 일시적 오류로 처리하고 4xx의 경우에만failed상태로 전환하는 로직이 필요합니다.
장기 개선 (재발 방지)#
- 상태 복구 메커니즘:
failed상태의 integration에 대해 일정 기간 후 자동으로 재시도하는 별도 크론 작업 또는 관리자 알림을 추가하여, 일시적 외부 장애로 인한 영구 비활성화를 방지해야 합니다. - Alerting: Integration 갱신 실패 시 관련 팀 또는 사용자에게 알림을 발송하여, 수동 개입이 필요한 경우 신속하게 대응할 수 있도록 합니다.
Monitoring#
- Integration 갱신 실패 모니터링:
service:cupixworks-worker status:error "failed to renew integration"
- BIM360 API 오류율 추적:
service:cupixworks-worker status:error "BIM360 refresh_token failed"
failed상태 integration 수 증가 알림 (DB 모니터링 또는 별도 헬스체크 크론)
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard
- 근거: Autodesk Forge API의 일시적 500 에러에 의한 단건 장애이며, 다른 리전/integration에는 영향이 없었습니다. 다만
failed상태 전환 후 자동 복구 경로가 없어 사용자 영향이 지속될 수 있으므로, 재시도 로직 추가가 권장됩니다.