we-promise/sure · error · Provider::Questrade::Error

not_found

not_found

Error message

Resource not found

What it means

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).

Source

Thrown at app/models/provider/questrade.rb:258

        source: self.class.name,
        provider_key: "questrade",
        metadata: { status: response.code, body: response.body.to_s.first(1000) }
      )
    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

  1. Log the full URL being requested and compare with one captured from a working session.
  2. Verify account_id is non-blank and comes from list_accounts output before interpolating it.
  3. Force a fresh exchange (drop the cached api_server) so the client gets the currently valid per-session host.
  4. Check Questrade API docs for endpoint removals if the path itself 404s with a known-good host.

Example fix

# before
provider.get_holdings(account_id: account.display_number) # wrong id -> 404

# after
api_id = provider.list_accounts[:accounts]
  .find { |a| a[:number] == account.display_number }&.dig(:number)
raise ArgumentError, "account not found via list_accounts" if api_id.blank?
provider.get_holdings(account_id: api_id)
Defensive patterns

Strategy: validation

Validate before calling

raise ArgumentError, "account_id required" if account_id.to_s.strip.blank?
account_id = account_id.to_s.strip
# and never hand-edit the persisted api_server string

Try / catch

begin
  provider.get_holdings(account_id: id)
rescue Provider::Questrade::Error => e
  if e.error_type == :not_found
    provider.exchange_token! # refresh per-session api_server host, then retry once
    retry
  end
  raise
end

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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