we-promise/sure · error · Provider::Snaptrade::ApiError

SnapTrade API error (#{operation}): HTTP #{response.status}

Error message

SnapTrade API error (#{operation}): HTTP #{response.status}

What it means

The fallback branch of Provider::Snaptrade.handle_response: any HTTP status other than 2xx, 401/403, 429, and 500-599 raises this generic ApiError with the operation name and status. The error object exposes status_code and response_body for diagnosis; 400-class and 404-class responses land here.

Source

Thrown at app/models/provider/snaptrade.rb:395

          JSON.parse(response.body)
        rescue JSON::ParserError
          raise ApiError.new("SnapTrade API error (#{operation}): invalid JSON response",
                             status_code: response.status, response_body: response.body)
        end
      else
        Rails.logger.error("SnapTrade API error (#{operation}): #{response.status}")
        case response.status
        when 401, 403
          mark_requires_update!
          raise AuthenticationError, "Authentication failed (#{operation}): HTTP #{response.status}"
        when 429
          raise ApiError.new("Rate limit exceeded. Please try again later.",
                             status_code: response.status, response_body: response.body)
        when 500..599
          raise ApiError.new("SnapTrade server error (#{response.status}). Please try again later.",
                             status_code: response.status, response_body: response.body)
        else
          raise ApiError.new("SnapTrade API error (#{operation}): HTTP #{response.status}",
                             status_code: response.status, response_body: response.body)
        end
      end
    end

    def api_connection
      @api_connection ||= Faraday.new do |faraday|
        faraday.options.timeout = 30
        faraday.options.open_timeout = 10
      end
    end

    def with_retries(operation_name, max_retries: MAX_RETRIES)
      retries = 0

      begin
        yield
      rescue Faraday::TimeoutError, Faraday::ConnectionFailed, Errno::ECONNRESET, Errno::ETIMEDOUT => e

View on GitHub (pinned to e69894adb9)

Solutions

  1. Inspect err.status_code and err.response_body - the body usually names the invalid parameter or missing resource
  2. 404: re-fetch the account list for the connection - the account may have been closed or re-keyed on re-link
  3. 400: validate request params (date formats, account_id shape) against the current SnapTrade API docs for that operation
  4. After a SnapTrade API version change, audit each operation name in the message against the new endpoint signatures
Defensive patterns

Strategy: try-catch

Validate before calling

def valid_snaptrade_account_id?(id)
  id.is_a?(String) && id.match?(/\A[0-9a-f-]{36}\z/i) # adjust to SnapTrade's id shape
end

Try / catch

begin
  snaptrade.get_positions(account_id: id)
rescue Provider::Snaptrade::ApiError => e
  if [400, 404, 410].include?(e.status_code)
    refresh_account_list!(e.response_body) # stale ids: re-sync from source of truth
  else
    raise
  end
end

Prevention

When it happens

Trigger: Calling a SnapTrade endpoint with a bad parameter (400), a deleted/unknown account_id (404), a removed endpoint after an API version bump (404/410), or any status the case statement does not special-case.

Common situations: Stored SnapTrade account reference stale after the user re-linked the connection (old account_id now 404s); request parameter changes after upgrading the snaptrade gem/API version; malformed query params (bad date format in startDate).

Related errors


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