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
- Exponentially back off and retry on :rate_limited (e.g. 30s, 2m, 8m) instead of failing the sync
- Serialize or add jitter to SophtronItem imports so concurrent jobs do not overlap
- Increase the polling interval for job status (Sophtron jobs take tens of seconds; poll every 5-10s not every 1s)
- 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
- Serialize SophtronItem imports (queue with concurrency 1) instead of parallel fan-out
- Poll job status at 5-10s intervals, not 1s
- Add jitter to scheduled syncs so many items do not start on the same tick
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.