{"record":{"id":"599d8bcafcb30a17","repo":"we-promise/sure","slug":"unknown-t-invest-instrument-symbol","errorCode":null,"errorMessage":"Unknown T-Invest instrument: #{symbol}","messagePattern":"Unknown T-Invest instrument: #(.+?)","errorType":"exception","errorClass":"Provider::TinkoffInvest::Error","httpStatus":null,"severity":"error","filePath":"app/models/provider/tinkoff_invest.rb","lineNumber":98,"sourceCode":"        next nil unless row[\"apiTradeAvailableFlag\"] # only surface instruments the API can actually price\n        next nil unless surfaced_kind(row[\"instrumentType\"])\n\n        Provider::SecurityConcept::Security.new(\n          symbol: row[\"ticker\"].to_s,\n          name: (row[\"name\"].presence || row[\"ticker\"]).to_s,\n          logo_url: nil, # FindInstrument carries no brand; logos come from #fetch_security_info\n          exchange_operating_mic: mic_for(row[\"classCode\"], row[\"exchange\"]),\n          country_code: row[\"countryOfRisk\"].presence,\n          currency: row[\"currency\"].to_s.upcase.presence\n        )\n      end.uniq { |s| [ s.symbol, s.exchange_operating_mic ] }\n    end\n  end\n\n  def fetch_security_info(symbol:, exchange_operating_mic:)\n    with_provider_response do\n      short = resolve_short(symbol, exchange_operating_mic)\n      raise Error, \"Unknown T-Invest instrument: #{symbol}\" if short.nil?\n\n      detail = instrument_detail(short[\"uid\"])\n\n      SecurityInfo.new(\n        symbol: short[\"ticker\"].to_s,\n        name: (detail[\"name\"].presence || short[\"name\"]).to_s,\n        links: nil, # T-Invest exposes no issuer website\n        logo_url: logo_url(detail.dig(\"brand\", \"logoName\")),\n        description: nil,\n        kind: surfaced_kind(short[\"instrumentType\"]),\n        exchange_operating_mic: mic_for(short[\"classCode\"], detail[\"exchange\"])\n      )\n    end\n  end\n\n  def fetch_security_price(symbol:, exchange_operating_mic:, date:)\n    with_provider_response do\n      historical = fetch_security_prices(","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/tinkoff_invest.rb#L80-L116","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"# before\ninfo = provider.fetch_security_info(symbol: symbol, exchange_operating_mic: mic)\n\n# after\nbegin\n  info = provider.fetch_security_info(symbol: symbol, exchange_operating_mic: mic)\nrescue Provider::TinkoffInvest::Error\n  fallback_info = other_provider.fetch_security_info(symbol: symbol, exchange_operating_mic: mic) if other_provider\n  raise unless fallback_info\n  fallback_info\nend","handlingStrategy":"validation","validationCode":"# Only ask T-Invest for instruments it actually lists\nprovider.find_instruments(security.symbol).any? { |r| r['ticker'].to_s.casecmp?(security.symbol.sub(/\\.(ME|MOEX|MISX|MCX)\\z/i, '')) }","typeGuard":"def t_invest_instrument?(find_instruments_rows, symbol)\n  bare = symbol.sub(/\\.(ME|MOEX|MISX|MCX)\\z/i, '')\n  find_instruments_rows.any? { |r| r['ticker'].to_s.casecmp?(bare) || r['isin'].to_s.casecmp?(bare) }\nend","tryCatchPattern":"begin\n  info = provider.fetch_security_info(symbol:, exchange_operating_mic:)\nrescue Provider::TinkoffInvest::Error => e\n  raise unless e.message.include?('Unknown T-Invest instrument')\n  security.update!(info_provider: 'moex_public')\nend","preventionTips":["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"],"tags":["tinkoff-invest","t-invest","unknown-symbol","instrument-resolution","securities"],"backgroundTag":"unknown-security-symbol","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}