we-promise/sure · warning · Provider::Brex::BrexError

rate_limited

rate_limited

Error message

Brex rate limit exceeded. Please try again later.

What it means

Raised by Provider::Brex#handle_response on HTTP 429: Brex's rate limit was exceeded and the request was not processed. It is transient by design — the same call succeeds after waiting — and get_paginated's tight loop (limit 1000, up to MAX_PAGES 25 pages per resource) can trip it when syncing many accounts. http_status 429 and trace_id are attached.

Source

Thrown at app/models/provider/brex.rb:224

      case response.code
      when 200
        parse_json(response.body)
      when 400
        Rails.logger.error "Brex API: bad request for #{path} trace_id=#{trace_id}"
        raise BrexError.new("Bad request to Brex API", :bad_request, http_status: 400, trace_id: trace_id)
      when 401
        Rails.logger.warn "Brex API: unauthorized for #{path} trace_id=#{trace_id}"
        raise BrexError.new("Invalid Brex API token or account permissions", :unauthorized, http_status: 401, trace_id: trace_id)
      when 403
        Rails.logger.warn "Brex API: access forbidden for #{path} trace_id=#{trace_id}"
        raise BrexError.new("Access forbidden - check Brex API token scopes", :access_forbidden, http_status: 403, trace_id: trace_id)
      when 404
        Rails.logger.warn "Brex API: resource not found for #{path} trace_id=#{trace_id}"
        raise BrexError.new("Brex resource not found", :not_found, http_status: 404, trace_id: trace_id)
      when 429
        Rails.logger.warn "Brex API: rate limited for #{path} trace_id=#{trace_id}"
        raise BrexError.new("Brex rate limit exceeded. Please try again later.", :rate_limited, http_status: 429, trace_id: trace_id)
      else
        Rails.logger.error "Brex API: unexpected response code=#{response.code} path=#{path} trace_id=#{trace_id}"
        raise BrexError.new("Failed to fetch data from Brex API: HTTP #{response.code}", :fetch_failed, http_status: response.code, trace_id: trace_id)
      end
    end

    def parse_json(body)
      return {} if body.blank?

      JSON.parse(body, symbolize_names: true)
    end

    def rfc3339_start_date(start_date)
      time =
        case start_date
        when Time
          start_date
        when DateTime

View on GitHub (pinned to e69894adb9)

Solutions

  1. Retry the failed call after an exponential backoff (start ~seconds, not milliseconds) — the request was not processed, so retry is safe
  2. Serialize or stagger Brex sync jobs per token so concurrent paginated loops don't stack
  3. Lengthen the sync interval or reduce the number of accounts synced per run
  4. If persistent, check the Brex dashboard for the token's quota tier and request a raise

Example fix

# before
client.get_cash_transactions(account_id, start_date: from)

# after
attempts = 0
begin
  client.get_cash_transactions(account_id, start_date: from)
rescue Provider::Brex::BrexError => e
  raise unless e.error_type == :rate_limited && (attempts += 1) <= 5
  sleep((2**attempts) + rand(2))
  retry
end
Defensive patterns

Strategy: retry

Validate before calling

# Cheap client-side throttle before a sync burst
class BrexRateLimiter
  def initialize(min_interval: 0.5) = (@min_interval = min_interval)
  def throttle! = (now = Process.clock_gettime(Process::CLOCK_MONOTONIC); sleep(@min_interval - (now - @last)) if @last && (now - @last) < @min_interval; @last = Process.clock_gettime(Process::CLOCK_MONOTONIC))
end

Type guard

def brex_rate_limited?(error)
  error.is_a?(Provider::Brex::BrexError) && error.error_type == :rate_limited
end

Try / catch

attempts = 0
begin
  client.get_accounts
rescue Provider::Brex::BrexError => e
  raise unless e.error_type == :rate_limited && (attempts += 1) <= 5
  sleep((2**attempts) + rand(2)) # exponential + jitter
  retry
end

Prevention

When it happens

Trigger: Several get_paginated loops running concurrently for the same token; frequent scheduled syncs (e.g. every minute) stacking up; a burst of page requests within one large transaction backfill exceeding the per-minute quota.

Common situations: Parallel background jobs syncing multiple Brex connections simultaneously, cron schedules drifting together, initial historical backfills that page heavily.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/f90fcca6c76c4aa1. Report an issue: GitHub.