{"record":{"id":"d803c0397ee1ae67","repo":"we-promise/sure","slug":"not-found-d803c0","errorCode":"not_found","errorMessage":"Resource not found","messagePattern":"Resource not found","errorType":"exception","errorClass":"Provider::Questrade::Error","httpStatus":404,"severity":"error","filePath":"app/models/provider/questrade.rb","lineNumber":258,"sourceCode":"        source: self.class.name,\n        provider_key: \"questrade\",\n        metadata: { status: response.code, body: response.body.to_s.first(1000) }\n      )\n    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":240,"sourceCodeEnd":269,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/questrade.rb#L240-L269","documentation":"Raised by Provider::Questrade#handle_response on HTTP 404: the path under api_server does not exist. Because api_server is a per-session host returned by the token exchange, a 404 usually means the path is malformed (bad account_id interpolation) or the api_server value is stale/wrong (e.g. missing a trailing segment or pointing at a retired host), rather than the account being absent (Questrade typically 403s foreign accounts).","triggerScenarios":"get_holdings/get_balances with a nil or malformed account_id producing 'v1/accounts//positions'; api_server persisted without its path prefix or with a typo after the exchange; calling a v1 endpoint that Questrade retired; using api_base before it ends with '/' (the method normalizes this, but bypassing it breaks paths).","commonSituations":"account_id stored as the display number instead of the API id; manual edits to the persisted api_server string; Questrade migrating api_server hosts for new sessions while old ones are cached indefinitely.","solutions":["Log the full URL being requested and compare with one captured from a working session.","Verify account_id is non-blank and comes from list_accounts output before interpolating it.","Force a fresh exchange (drop the cached api_server) so the client gets the currently valid per-session host.","Check Questrade API docs for endpoint removals if the path itself 404s with a known-good host."],"exampleFix":"# before\nprovider.get_holdings(account_id: account.display_number) # wrong id -> 404\n\n# after\napi_id = provider.list_accounts[:accounts]\n  .find { |a| a[:number] == account.display_number }&.dig(:number)\nraise ArgumentError, \"account not found via list_accounts\" if api_id.blank?\nprovider.get_holdings(account_id: api_id)","handlingStrategy":"validation","validationCode":"raise ArgumentError, \"account_id required\" if account_id.to_s.strip.blank?\naccount_id = account_id.to_s.strip\n# and never hand-edit the persisted api_server string","typeGuard":null,"tryCatchPattern":"begin\n  provider.get_holdings(account_id: id)\nrescue Provider::Questrade::Error => e\n  if e.error_type == :not_found\n    provider.exchange_token! # refresh per-session api_server host, then retry once\n    retry\n  end\n  raise\nend","preventionTips":["Always source account IDs from list_accounts output, never the web dashboard.","When a 404 appears for everything, suspect the stored api_server before the IDs."],"tags":["questrade","http-404","api-server","account-id","brokerage-api"],"backgroundTag":"http-404-not-found","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}