Module: Aidp::Providers::ErrorTaxonomy

Defined in:
lib/aidp/providers/error_taxonomy.rb

Overview

ErrorTaxonomy defines the five standardized error categories that all providers use for consistent error handling, retry logic, and escalation.

Categories:

  • rate_limited: Provider is rate-limiting requests (switch provider immediately)

  • auth_expired: Authentication credentials are invalid or expired (escalate or switch)

  • quota_exceeded: Usage quota has been exceeded (switch provider)

  • transient: Temporary error that may resolve on retry (retry with backoff)

  • permanent: Permanent error that won’t resolve with retry (escalate or abort)

Constant Summary collapse

RATE_LIMITED =

Error category constants

:rate_limited
AUTH_EXPIRED =
:auth_expired
QUOTA_EXCEEDED =
:quota_exceeded
TRANSIENT =
:transient
PERMANENT =
:permanent
CATEGORIES =

All valid error categories

[
  RATE_LIMITED,
  AUTH_EXPIRED,
  QUOTA_EXCEEDED,
  TRANSIENT,
  PERMANENT
].freeze
DEFAULT_PATTERNS =

Default error patterns for common error messages Providers can override these with provider-specific patterns

{
  rate_limited: [
    /rate.?limit/i,
    /too.?many.?requests/i,
    /429/,
    /throttl(ed|ing)/i,
    /request.?limit/i,
    /requests.?per.?minute/i,
    /rpm.?exceeded/i
  ],
  auth_expired: [
    /auth(entication|orization).?(fail(ed|ure)|error)/i,
    /invalid.?(api.?key|token|credential)/i,
    /expired.?(api.?key|token|credential)/i,
    /unauthorized/i,
    /401/,
    /403/,
    /permission.?denied/i,
    /access.?denied/i
  ],
  quota_exceeded: [
    /quota.?(exceed(ed)?|limit|exhausted)/i,
    /usage.?limit/i,
    /billing.?limit/i,
    /credit.?limit/i,
    /insufficient.?quota/i,
    /usage.?cap/i
  ],
  transient: [
    /timeout/i,
    /timed?.?out/i,
    /connection.?(reset|refused|lost|closed)/i,
    /temporary.?error/i,
    /try.?again/i,
    /service.?unavailable/i,
    /503/,
    /502/,
    /504/,
    /gateway.?timeout/i,
    /network.?error/i,
    /socket.?error/i,
    /connection.?error/i,
    /broken.?pipe/i,
    /host.?unreachable/i
  ],
  permanent: [
    /invalid.?(model|parameter|request|input)/i,
    /unsupported.?(operation|feature|model)/i,
    /not.?found/i,
    /404/,
    /bad.?request/i,
    /400/,
    /malformed/i,
    /syntax.?error/i,
    /validation.?error/i,
    /model.?not.?available/i,
    /model.?deprecated/i
  ]
}.freeze
RETRY_POLICIES =

Retry policy for each category

{
  rate_limited: {
    retry: false,
    switch_provider: true,
    escalate: false,
    backoff_strategy: :none
  },
  auth_expired: {
    retry: false,
    switch_provider: true,
    escalate: true,
    backoff_strategy: :none
  },
  quota_exceeded: {
    retry: false,
    switch_provider: true,
    escalate: false,
    backoff_strategy: :none
  },
  transient: {
    retry: true,
    switch_provider: false,
    escalate: false,
    backoff_strategy: :exponential
  },
  permanent: {
    retry: false,
    switch_provider: false,
    escalate: true,
    backoff_strategy: :none
  }
}.freeze

Class Method Summary collapse

Class Method Details

.backoff_strategy(category) ⇒ Symbol

Get backoff strategy for a category

Parameters:

  • category (Symbol)

    error category

Returns:

  • (Symbol)

    backoff strategy (:none, :linear, :exponential)



189
190
191
192
# File 'lib/aidp/providers/error_taxonomy.rb', line 189

def self.backoff_strategy(category)
  policy = retry_policy(category)
  policy[:backoff_strategy] || :none
end

.classify_message(message) ⇒ Symbol

Classify an error message using default patterns

Parameters:

  • message (String)

    error message

Returns:

  • (Symbol)

    error category



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/aidp/providers/error_taxonomy.rb', line 146

def self.classify_message(message)
  return :transient if message.nil? || message.empty?

  message_lower = message.downcase

  # Check each category's patterns
  DEFAULT_PATTERNS.each do |category, patterns|
    patterns.each do |pattern|
      return category if message_lower.match?(pattern)
    end
  end

  # Default to transient for unknown errors
  :transient
end

.retry_policy(category) ⇒ Hash

Get retry policy for a category

Parameters:

  • category (Symbol)

    error category

Returns:

  • (Hash)

    retry policy configuration



139
140
141
# File 'lib/aidp/providers/error_taxonomy.rb', line 139

def self.retry_policy(category)
  RETRY_POLICIES[category] || RETRY_POLICIES[:transient]
end

.retryable?(category) ⇒ Boolean

Check if an error category is retryable

Parameters:

  • category (Symbol)

    error category

Returns:

  • (Boolean)

    true if should retry



165
166
167
168
# File 'lib/aidp/providers/error_taxonomy.rb', line 165

def self.retryable?(category)
  policy = retry_policy(category)
  policy[:retry] == true
end

.should_escalate?(category) ⇒ Boolean

Check if an error category should be escalated

Parameters:

  • category (Symbol)

    error category

Returns:

  • (Boolean)

    true if should escalate



181
182
183
184
# File 'lib/aidp/providers/error_taxonomy.rb', line 181

def self.should_escalate?(category)
  policy = retry_policy(category)
  policy[:escalate] == true
end

.should_switch_provider?(category) ⇒ Boolean

Check if an error category should trigger provider switch

Parameters:

  • category (Symbol)

    error category

Returns:

  • (Boolean)

    true if should switch provider



173
174
175
176
# File 'lib/aidp/providers/error_taxonomy.rb', line 173

def self.should_switch_provider?(category)
  policy = retry_policy(category)
  policy[:switch_provider] == true
end

.valid_category?(category) ⇒ Boolean

Check if a category is valid

Parameters:

  • category (Symbol)

    category to check

Returns:

  • (Boolean)

    true if valid



132
133
134
# File 'lib/aidp/providers/error_taxonomy.rb', line 132

def self.valid_category?(category)
  CATEGORIES.include?(category)
end