we-promise/sure · error · Provider::Tiingo::Error
Could not determine currency for #{symbol} from Tiingo searc
Error message
Could not determine currency for #{symbol} from Tiingo search What it means
fetch_currency_for_symbol is the fallback when the 'tiingo:currency:<SYMBOL>' cache is cold: it re-queries the search endpoint and resolves a currency via best_match_for_ticker -> currency_for_country(countryCode) using ISO 4217 data. It deliberately raises (rather than defaulting) when it cannot determine a currency, to avoid silently mislabeling prices. The raise means either search returned no usable array/match, or the match's countryCode is missing/mapped to no currency.
Source
Thrown at app/models/provider/tiingo.rb:300
response = client.get("#{base_url}/tiingo/utilities/search") do |req|
req.params["query"] = symbol
end
parsed = JSON.parse(response.body)
check_api_error!(parsed)
if parsed.is_a?(Array)
match = best_match_for_ticker(parsed, symbol)
currency = currency_for_country(match&.dig("countryCode"))
if currency.present?
Rails.cache.write("tiingo:currency:#{symbol.upcase}", currency, expires_in: 24.hours)
return currency
end
end
raise Error, "Could not determine currency for #{symbol} from Tiingo search"
end
def map_exchange_to_mic(exchange_name)
return nil if exchange_name.blank?
TIINGO_EXCHANGE_TO_MIC[exchange_name.strip] || exchange_name.strip
end
# Tiingo's search/utilities response never includes a priceCurrency field
# (confirmed against the live API), only countryCode. Resolve the currency
# via the countries gem's ISO 4217 data (already used for country
# resolution in Provider::TwelveData) instead of hand-maintaining a
# per-provider allowlist.
def currency_for_country(country_code)
return nil if country_code.blank?
ISO3166::Country.new(country_code.strip)&.currency_code
end
# Tiingo's search endpoint can return multiple entries sharing the exactView on GitHub (pinned to e69894adb9)
Solutions
- Identify the symbol class: if it is crypto/forex, price it via an endpoint/provider that returns the currency directly instead of inferring from country
- Retry later -- a missing countryCode is often a transient upstream gap; the 24h cache means it re-attempts next day
- If a legitimate countryCode is unmapped, extend currency_for_country's ISO 4217 mapping
Example fix
# before currency = Rails.cache.read(cache_key) || fetch_currency_for_symbol(symbol) # after currency = Rails.cache.read(cache_key) currency ||= begin fetch_currency_for_symbol(symbol) rescue Provider::Tiingo::Error raise if crypto_or_fx?(symbol) 'USD' # explicit, logged fallback for equities with missing country data end
Defensive patterns
Strategy: fallback
Validate before calling
# Warm the currency cache via search before bulk pricing
provider.search_securities(symbol) unless Rails.cache.read("tiingo:currency:#{symbol.upcase}") Try / catch
begin currency = fetch_currency_for_symbol(symbol) rescue Provider::Tiingo::Error currency = nil # skip symbol this round; do not guess end
Prevention
- Call search_securities first: it pre-populates tiingo:currency:<SYMBOL> for 24h and avoids the fallback entirely
- Route crypto/forex symbols to providers that report currency directly
When it happens
Trigger: fetch_security_prices for a symbol whose currency cache entry expired (24h TTL) and whose search results have no countryCode, an unrecognized country code, or zero matches. Non-country instruments (crypto tickers like BTC-USD on the daily endpoint) commonly have no countryCode and hit this path.
Common situations: Crypto/derivative symbols routed through the equities daily endpoint; Tiingo search dropping countryCode for small exchanges; first-of-day fetch after TTL expiry racing an upstream data gap.
Related errors
- Unexpected response format from search endpoint
- Invalid currency code '#{currency_value}' for <%= class_name
- Invalid currency code '#{currency_value}' for <%= class_name
- No data returned from search endpoint
- No prices found for security #{symbol} on date #{date}
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/61191739eb2ab8c7.
Report an issue: GitHub.