we-promise/sure · error · Provider::TinkoffInvest::Error
Unknown T-Invest instrument: #{symbol}
Error message
Unknown T-Invest instrument: #{symbol} What it means
fetch_security_info first resolves the symbol via resolve_short (InstrumentsService/FindInstrument, cached with skip_nil so misses aren't cached). resolve_short returns nil when FindInstrument yields no rows, or no rows whose instrumentType maps to a surfaced kind. fetch_security_info then raises Error 'Unknown T-Invest instrument: <symbol>'. Exchange suffixes like .ME/.MOEX/.MISX/.MCX are stripped automatically before the lookup, so the remaining causes are genuinely unknown tickers or unsupported instrument types.
Source
Thrown at app/models/provider/tinkoff_invest.rb:98
next nil unless row["apiTradeAvailableFlag"] # only surface instruments the API can actually price
next nil unless surfaced_kind(row["instrumentType"])
Provider::SecurityConcept::Security.new(
symbol: row["ticker"].to_s,
name: (row["name"].presence || row["ticker"]).to_s,
logo_url: nil, # FindInstrument carries no brand; logos come from #fetch_security_info
exchange_operating_mic: mic_for(row["classCode"], row["exchange"]),
country_code: row["countryOfRisk"].presence,
currency: row["currency"].to_s.upcase.presence
)
end.uniq { |s| [ s.symbol, s.exchange_operating_mic ] }
end
end
def fetch_security_info(symbol:, exchange_operating_mic:)
with_provider_response do
short = resolve_short(symbol, exchange_operating_mic)
raise Error, "Unknown T-Invest instrument: #{symbol}" if short.nil?
detail = instrument_detail(short["uid"])
SecurityInfo.new(
symbol: short["ticker"].to_s,
name: (detail["name"].presence || short["name"]).to_s,
links: nil, # T-Invest exposes no issuer website
logo_url: logo_url(detail.dig("brand", "logoName")),
description: nil,
kind: surfaced_kind(short["instrumentType"]),
exchange_operating_mic: mic_for(short["classCode"], detail["exchange"])
)
end
end
def fetch_security_price(symbol:, exchange_operating_mic:, date:)
with_provider_response do
historical = fetch_security_prices(View on GitHub (pinned to e69894adb9)
Solutions
- Verify the instrument exists in T-Invest (invest.tinkoff.ru or the API's FindInstrument) using the bare SECID without exchange suffix
- If it exists but raises, check its instrumentType -- only kinds surfaced_kind maps are supported; unsupported types must use another provider
- Retry once for a transient empty catalog response; skip_nil caching means a later successful lookup will fill the cache
Example fix
# before info = provider.fetch_security_info(symbol: symbol, exchange_operating_mic: mic) # after begin info = provider.fetch_security_info(symbol: symbol, exchange_operating_mic: mic) rescue Provider::TinkoffInvest::Error fallback_info = other_provider.fetch_security_info(symbol: symbol, exchange_operating_mic: mic) if other_provider raise unless fallback_info fallback_info end
Defensive patterns
Strategy: validation
Validate before calling
# Only ask T-Invest for instruments it actually lists
provider.find_instruments(security.symbol).any? { |r| r['ticker'].to_s.casecmp?(security.symbol.sub(/\.(ME|MOEX|MISX|MCX)\z/i, '')) } Type guard
def t_invest_instrument?(find_instruments_rows, symbol)
bare = symbol.sub(/\.(ME|MOEX|MISX|MCX)\z/i, '')
find_instruments_rows.any? { |r| r['ticker'].to_s.casecmp?(bare) || r['isin'].to_s.casecmp?(bare) }
end Try / catch
begin
info = provider.fetch_security_info(symbol:, exchange_operating_mic:)
rescue Provider::TinkoffInvest::Error => e
raise unless e.message.include?('Unknown T-Invest instrument')
security.update!(info_provider: 'moex_public')
end Prevention
- Resolve instruments via search_securities once and persist the resolved symbol/MIC before detail/price calls
- Remember misses are uncached (skip_nil): one transient failure shouldn't be treated as permanent -- verify before excluding
When it happens
Trigger: Calling fetch_security_info(symbol: 'SBER.MOEX', exchange_operating_mic: 'XMOS') works, but a foreign ticker like 'AAPL', a typo, or an instrument whose only listings have unsupported instrumentType raises. Also possible: FindInstrument returned a transient empty response (not cached thanks to skip_nil).
Common situations: Securities synced from other providers (MOEX resolver, manual entry) that T-Invest doesn't list; delisted instruments; tickers valid on SPB but not tradable via this API account; brand-new listings not yet in T-Invest's instrument catalog.
Related errors
- No T-Invest price for #{symbol} on #{date}
- Missing or invalid T-Invest bond nominal for #{symbol}
- record_not_found
- record_not_found
- parsed["Information"]
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/599d8bcafcb30a17.
Report an issue: GitHub.