{"record":{"id":"64f45ec7a9c205bc","repo":"we-promise/sure","slug":"server-error-64f45e","errorCode":"server_error","errorMessage":"Questrade server error (#{response.code}). Please try again later.","messagePattern":"Questrade server error \\(#(.+?)\\)\\. Please try again later\\.","errorType":"exception","errorClass":"Provider::Questrade::RetryableResponseError","httpStatus":null,"severity":"error","filePath":"app/models/provider/questrade.rb","lineNumber":262,"sourceCode":"    end\n\n    def handle_response(response)\n      case response.code\n      when 200, 201\n        JSON.parse(response.body, symbolize_names: true)\n      when 400\n        capture_response_error(\"bad_request\", response)\n        raise Error.new(\"Questrade bad request (#{response.code})\", :bad_request)\n      when 401\n        raise AuthenticationError.new(\"Invalid or expired Questrade credentials\", :unauthorized)\n      when 403\n        raise AuthenticationError.new(\"Access forbidden - check your permissions\", :access_forbidden)\n      when 404\n        raise Error.new(\"Resource not found\", :not_found)\n      when 429\n        raise RetryableResponseError.new(\"Questrade rate limit exceeded. Please try again later.\", :rate_limited)\n      when 500..599\n        raise RetryableResponseError.new(\"Questrade server error (#{response.code}). Please try again later.\", :server_error)\n      else\n        capture_response_error(\"unexpected_response\", response)\n        raise Error.new(\"Questrade unexpected response (#{response.code})\", :unknown)\n      end\n    end\nend\n","sourceCodeStart":244,"sourceCodeEnd":269,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/questrade.rb#L244-L269","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\ndata = provider.get_balances(account_id: id)\n\n# after\nattempts = 0\nbegin\n  data = provider.get_balances(account_id: id)\nrescue Provider::Questrade::RetryableResponseError => e\n  raise if e.error_type != :server_error || (attempts += 1) > 3\n  SyncJob.perform_in(5.minutes * attempts, item.id)\n  return\nend","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  provider.get_balances(account_id: id)\nrescue Provider::Questrade::RetryableResponseError => e\n  raise if e.error_type != :server_error\n  SyncJob.perform_in(10.minutes, item.id) # transient 5xx; SDK does not retry HTTP-level\nend","preventionTips":["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."],"tags":["questrade","http-5xx","server-error","retryable","brokerage-api"],"backgroundTag":"http-5xx-server-error","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}