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

unauthorized

unauthorized

Error message

Invalid Brex API token or account permissions

What it means

Raised by Provider::Brex#handle_response on HTTP 401: the Bearer token sent in auth_headers was rejected by Brex, so every endpoint call will fail the same way. The token is taken verbatim from the Provider::Brex constructor (stripped of whitespace) and never refreshed by this client. http_status 401 and the X-Brex-Trace-Id are attached to the error.

Source

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

      {
        "Authorization" => "Bearer #{token}",
        "Content-Type" => "application/json",
        "Accept" => "application/json"
      }
    end

    def handle_response(response, path:)
      trace_id = brex_trace_id(response)

      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?

View on GitHub (pinned to e69894adb9)

Solutions

  1. Issue a fresh API token in the Brex dashboard and update the stored credential
  2. Verify the token matches the environment: staging tokens only work with base_url https://api-staging.brex.com
  3. Confirm the token has no surrounding whitespace/newlines (the constructor strips edges, but internal characters must be intact)
  4. Check the Rails log 'Brex API: unauthorized for <path> trace_id=...' and confirm the same failure across all endpoints (a 401 on only one endpoint is actually a scopes problem, see :access_forbidden)

Example fix

# before
Provider::Brex.new(ENV["BREX_TOKEN"].to_s) # nil -> "" silently, fails later with 401

# after
token = ENV["BREX_TOKEN"].to_s.strip
raise ArgumentError, "BREX_TOKEN is missing or empty" if token.empty?
Provider::Brex.new(token)
Defensive patterns

Strategy: validation

Validate before calling

def brex_token_configured?(token)
  token.is_a?(String) && !token.strip.empty?
end

Type guard

def brex_unauthorized?(error)
  error.is_a?(Provider::Brex::BrexError) && error.error_type == :unauthorized
end

Try / catch

begin
  client.get_cash_accounts
rescue Provider::Brex::BrexError => e
  raise unless e.error_type == :unauthorized
  connection.update!(status: "reauth_required") # never retry; flag for credential rotation
  raise
end

Prevention

When it happens

Trigger: The stored token was revoked or expired in the Brex dashboard; a staging token is used against https://api.brex.com (or vice versa); the token was truncated or mangled when copied; an empty/placeholder token was passed to Provider::Brex.new.

Common situations: Rotating API credentials and forgetting to update the encrypted store, environment mismatch between token and base_url, whitespace or newline contamination from copy-paste or .env handling.

Related errors


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