we-promise/sure · warning · Provider::Sophtron::Error

rate_limited

rate_limited

Error message

Sophtron rate limit exceeded. Please try again later.

What it means

Provider::Sophtron raises this Error with error_type=:rate_limited when Sophtron responds HTTP 429. Sophtron throttles per API user; the message advises retrying later, and the response body is attached as details.

Source

Thrown at app/models/provider/sophtron.rb:365

    def handle_response(response, parse_json: true)
      body = response.body.to_s

      case response.code.to_i
      when 200, 201, 204
        return {} if body.strip.blank?

        parse_json ? JSON.parse(body, symbolize_names: true) : parse_optional_json(body)
      when 400
        raise Error.new("Bad request to Sophtron API: #{body}", :bad_request, details: body)
      when 401
        raise Error.new("Invalid Sophtron User ID or Access Key", :unauthorized, details: body)
      when 403
        raise Error.new("Access forbidden by Sophtron", :access_forbidden, details: body)
      when 404
        raise Error.new("Sophtron resource not found", :not_found, details: body)
      when 429
        raise Error.new("Sophtron rate limit exceeded. Please try again later.", :rate_limited, details: body)
      else
        raise Error.new(
          "Sophtron API request failed: #{response.code} #{response.message} - #{body}",
          :fetch_failed,
          details: body
        )
      end
    rescue JSON::ParserError => e
      raise Error.new("Invalid JSON response from Sophtron API: #{e.message}", :invalid_response, details: body)
    end

    def parse_optional_json(body)
      JSON.parse(body, symbolize_names: true)
    rescue JSON::ParserError
      body
    end

    def normalize_base_url(value)

View on GitHub (pinned to e69894adb9)

Solutions

  1. Exponentially back off and retry on :rate_limited (e.g. 30s, 2m, 8m) instead of failing the sync
  2. Serialize or add jitter to SophtronItem imports so concurrent jobs do not overlap
  3. Increase the polling interval for job status (Sophtron jobs take tens of seconds; poll every 5-10s not every 1s)
  4. If quota is chronically hit, request a rate-limit increase from Sophtron or use separate API users per environment

Example fix

# before
result = provider.get_accounts(customer_id)

# after
result = with_backoff { provider.get_accounts(customer_id) }

def with_backoff(attempts: 4)
  yield
rescue Provider::Sophtron::Error => e
  raise unless e.error_type == :rate_limited && attempts > 1
  sleep(2 ** (5 - attempts))
  retry if (attempts -= 1) > 0
  raise
end
Defensive patterns

Strategy: retry

Validate before calling

# Rate limits cannot be pre-validated; pre-throttle instead
SyncThrottler.acquire(:sophtron) # token bucket, 1 req / interval, before each provider call

Type guard

def sophtron_rate_limited?(err)
  err.is_a?(Provider::Sophtron::Error) && e.error_type == :rate_limited
end

Try / catch

retries = 0
begin
  provider.get_transactions(account_id)
rescue Provider::Sophtron::Error => e
  retry if e.error_type == :rate_limited && (retries += 1) <= 4 && sleep(2**retries * 15)
  raise
end

Prevention

When it happens

Trigger: Bursty job polling (hammering get_job every second while a bank login completes); syncing many SophtronItems in parallel from one scheduled job; re-importing transaction history for several accounts simultaneously under a single Sophtron API user.

Common situations: A background cron/Sidekiq schedule that fan-outs imports without jitter; adding several institutions right after another; production and staging sharing one Sophtron API user so combined traffic trips the quota.

Related errors


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