we-promise/sure · error · Provider::Questrade::ConfigurationError
missing_credentials
missing_credentials
Error message
Refresh token is required
What it means
Provider::Questrade's constructor calls validate_configuration!, which raises ConfigurationError(:missing_credentials) when the refresh_token keyword is nil or blank. It fires immediately at Provider::Questrade.new, before any network call — the client refuses to operate without a refresh token because Questrade has no other auth path.
Source
Thrown at app/models/provider/questrade.rb:116
activities.concat(Array(page[:activities]))
window_start = window_end + 1
end
{ activities: activities }
end
private
RETRYABLE_ERRORS = [
SocketError, Net::OpenTimeout, Net::ReadTimeout,
Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::ETIMEDOUT, EOFError
].freeze
MAX_RETRIES = 3
INITIAL_RETRY_DELAY = 2 # seconds
def validate_configuration!
raise ConfigurationError.new("Refresh token is required", :missing_credentials) if @refresh_token.blank?
end
def get_json(path, query: {})
ensure_authenticated!
with_retries(path) do
response = self.class.get("#{api_base}#{path}", headers: auth_headers, query: query)
# Access token can expire mid-sync; refresh once and retry on 401.
if response.code == 401
authenticate!(force: true)
response = self.class.get("#{api_base}#{path}", headers: auth_headers, query: query)
end
handle_response(response)
end
end
# Exchange the refresh token unless we already hold a valid access token.
def ensure_authenticated!
authenticate! if @access_token.nil? || @access_expires_at.nil? || Time.current >= @access_expires_atView on GitHub (pinned to e69894adb9)
Solutions
- Guard before constructing: check the item's stored questrade refresh token is present, and skip/queue the sync if blank.
- Verify you pass refresh_token: (snake_case keyword) and that the persisted field actually maps to it.
- If the token is genuinely missing, route the user through Questrade authorization to obtain the first refresh token.
- Search for code paths that instantiate the provider from partial or stale item state (e.g. after failed onboarding) and add presence checks there.
Example fix
# before
provider = Provider::Questrade.new(
refresh_token: item.settings["refresh_token"], # nil -> raises in initialize
api_server: item.settings["api_server"]
)
# after
token = item.settings["refresh_token"]
if token.blank?
Rails.logger.info "Questrade item #{item.id} has no refresh token; re-auth required"
next
end
provider = Provider::Questrade.new(refresh_token: token, api_server: item.settings["api_server"]) Defensive patterns
Strategy: validation
Validate before calling
token = item.settings["refresh_token"].to_s raise ArgumentError, "Questrade refresh token missing" if token.blank? provider = Provider::Questrade.new(refresh_token: token, api_server: item.settings["api_server"])
Try / catch
begin provider = Provider::Questrade.new(refresh_token: token, on_token_refresh: persister) rescue Provider::Questrade::ConfigurationError => e raise unless e.error_type == :missing_credentials item.flag_reauthorization_required!(e) end
Prevention
- Presence-check the stored refresh token in the job's guard clause so the sync never constructs a half-configured client.
- Persist the very first refresh token as part of completing the OAuth return flow, before any sync is enqueued.
When it happens
Trigger: Provider::Questrade.new(refresh_token: nil) or refresh_token: "" — e.g. the persisted Questrade item has no token yet (user never finished authorization), the DB column was cleared, or a hash key typo ('refreshToken') yields nil via the missing default.
Common situations: Sync job firing before the OAuth return flow stored the first refresh token; seed/fixture data without a token; a migration wiping encrypted credentials; reading the wrong attribute off the item model (e.g. api_key instead of the questrade refresh token).
Related errors
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/2a09ece1f5c73fb1.
Report an issue: GitHub.