we-promise/sure · error · Provider::Tiingo::Error
API error: #{detail}
Error message
API error: #{detail} What it means
The sibling branch of check_api_error!: the response is a Hash with a non-empty 'detail' string that does NOT mention rate limits, so it is raised verbatim as Error 'API error: <detail>'. Typical Tiingo details include 'Api token invalid.', 'Forbidden', 'Not Found' (unknown symbol for that endpoint), and account messages. This is Tiingo rejecting the request with a human-readable reason; the code preserves it exactly.
Source
Thrown at app/models/provider/tiingo.rb:342
# when a US entry exists for the ticker, that's the one actually backing
# the price data. Fall back to the first match otherwise.
def best_match_for_ticker(results, ticker)
return nil if ticker.blank?
matches = results.select { |s| s["ticker"]&.upcase == ticker.upcase }
matches.find { |s| s["countryCode"] == "US" } || matches.first
end
def check_api_error!(parsed)
return unless parsed.is_a?(Hash) && parsed["detail"].present?
detail = parsed["detail"]
if detail.downcase.include?("rate limit") || detail.downcase.include?("too many")
raise RateLimitError, detail
end
raise Error, "API error: #{detail}"
end
end
View on GitHub (pinned to e69894adb9)
Solutions
- Read the detail: 'Api token invalid' -> fix/renew the key; 'Not Found' -> verify the symbol via search_securities; 'Forbidden' -> check plan entitlements
- Verify connectivity at setup with healthy? (GET /tiingo/daily/AAPL) in a console for that key
- Trim whitespace on the stored key and confirm it's set in the environment the job/web process actually uses
Example fix
# before provider = Provider::Tiingo.new(ENV['TIINGO_API_KEY']) provider.fetch_security_prices(...) # after (fail fast at configuration time) provider = Provider::Tiingo.new(ENV['TIINGO_API_KEY'].to_s.strip) raise Provider::Tiingo::Error, 'Tiingo API key missing' if provider.respond_to?(:healthy?) && !provider.healthy? provider.fetch_security_prices(...)
Defensive patterns
Strategy: try-catch
Validate before calling
# Validate the key and endpoint at configuration time raise 'TIINGO_API_KEY missing' if ENV['TIINGO_API_KEY'].blank?
Try / catch
begin
provider.fetch_security_prices(...)
rescue Provider::Tiingo::Error => e
raise if e.message.include?('rate limit')
provider.mark_unhealthy(reason: e.message) # 'Api token invalid', 'Not Found', etc.
end Prevention
- Strip whitespace from the API key at construction
- Run healthy? after key changes to catch invalid tokens at setup rather than mid-sync
When it happens
Trigger: Calling any Tiingo endpoint with a bad/empty/expired api key ('Api token invalid'), a ticker that doesn't exist on that endpoint ('Not Found'), insufficient plan access ('Forbidden'), or an account-level notice. The constructor Provider::Tiingo.new(api_key) does no up-front validation, so the first API call surfaces it.
Common situations: Wrong or placeholder TIINGO_API_KEY in env; key revoked after plan expiry; symbol typo or delisted ticker; endpoint not included in the current plan; trailing whitespace/newline pasted into the key.
Related errors
- unauthorized
- unauthorized
- Unauthorized
- Unauthorized - check your API key and secret
- Either API token or all three username/document/password cre
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/2ea7c16cd6e28bd7.
Report an issue: GitHub.