we-promise/sure · error · Provider::Questrade::RetryableResponseError
server_error
server_error
Error message
Questrade server error (#{response.code}). Please try again later. What it means
Raised by Provider::Questrade#handle_response for any 5xx status, wrapped in RetryableResponseError(:server_error) to mark it transient. Questrade's api_server hosts occasionally return 500-503 during incidents or under load; the client deliberately distinguishes these from fatal errors so callers can back off and retry the same call.
Source
Thrown at app/models/provider/questrade.rb:262
end
def handle_response(response)
case response.code
when 200, 201
JSON.parse(response.body, symbolize_names: true)
when 400
capture_response_error("bad_request", response)
raise Error.new("Questrade bad request (#{response.code})", :bad_request)
when 401
raise AuthenticationError.new("Invalid or expired Questrade credentials", :unauthorized)
when 403
raise AuthenticationError.new("Access forbidden - check your permissions", :access_forbidden)
when 404
raise Error.new("Resource not found", :not_found)
when 429
raise RetryableResponseError.new("Questrade rate limit exceeded. Please try again later.", :rate_limited)
when 500..599
raise RetryableResponseError.new("Questrade server error (#{response.code}). Please try again later.", :server_error)
else
capture_response_error("unexpected_response", response)
raise Error.new("Questrade unexpected response (#{response.code})", :unknown)
end
end
end
View on GitHub (pinned to e69894adb9)
Solutions
- Catch RetryableResponseError and retry with exponential backoff over minutes (the internal with_retries does NOT cover HTTP 5xx — only socket-level errors — so the caller must retry).
- Check Questrade status communications if 5xxs persist across accounts.
- Prefer scheduling the retry (perform_in) over inline sleeping in web/job processes.
- Keep requests lean (chunked activity windows via get_activities) to reduce upstream processing time.
Example fix
# before data = provider.get_balances(account_id: id) # after attempts = 0 begin data = provider.get_balances(account_id: id) rescue Provider::Questrade::RetryableResponseError => e raise if e.error_type != :server_error || (attempts += 1) > 3 SyncJob.perform_in(5.minutes * attempts, item.id) return end
Defensive patterns
Strategy: retry
Try / catch
begin provider.get_balances(account_id: id) rescue Provider::Questrade::RetryableResponseError => e raise if e.error_type != :server_error SyncJob.perform_in(10.minutes, item.id) # transient 5xx; SDK does not retry HTTP-level end
Prevention
- Remember with_retries only covers socket errors — build an outer backoff for 5xx.
- Spread scheduled syncs to avoid all accounts hammering during the same incident window.
- Alert when server_error repeats across days for one api_server host.
When it happens
Trigger: get_holdings/get_balances/get_activities during a Questrade incident (5xx on api_server); heavy end-of-day activity queries timing out upstream as 502/504; intermittent 500s on specific symbols during market hours.
Common situations: Sync jobs landing inside Questrade maintenance windows; regional api_server instability for a session's assigned host; retrying immediately after a first 5xx and cascading.
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/64f45ec7a9c205bc.
Report an issue: GitHub.