we-promise/sure · error · Provider::YahooFinance::Error

No chart data found for currency pair #

Error message

No chart data found for currency pair #{from}/#{to}

What it means

Provider::YahooFinance::Error raised in fetch_exchange_rates when neither the direct currency pair chart (e.g. EURUSD=X) nor the inverse pair (USDEUR=X) returned any data for the range. The API responded but the chart payload was empty, so no rates can be built.

Solutions

  1. Verify the pair exists on finance.yahoo.com (search 'EURTRY=X' style symbols) and use an explicit symbol if needed
  2. Route through a cross via USD: compute rate as EURUSD × USDTRY when the direct pair is missing (fetch each leg separately)
  3. Widen or shift the date range into the pair's available history
  4. Fall back to a dedicated FX provider for uncovered pairs

Example fix

// before
rates = provider.fetch_exchange_rates(from: "TRY", to: "KRW", start_date: s, end_date: e)

// after
begin
  rates = provider.fetch_exchange_rates(from: "TRY", to: "KRW", start_date: s, end_date: e)
rescue Provider::YahooFinance::Error => e
  raise unless e.message.include?("No chart data found")
  usd_base = provider.fetch_exchange_rates(from: "TRY", to: "USD", start_date: s, end_date: e).data.index_by(&:date)
  rates = provider.fetch_exchange_rates(from: "USD", to: "KRW", start_date: s, end_date: e).data.map do |r|
    Rate.new(date: r.date, from: "TRY", to: "KRW", rate: r.rate / usd_base[r.date].rate)
  end
end
Defensive patterns

Strategy: fallback

Validate before calling

return Rate.new(date:, from:, to:, rate: 1.0) if from == to # same-currency pairs never need Yahoo

Try / catch

begin
  provider.fetch_exchange_rates(from:, to:, start_date:, end_date:)
rescue Provider::YahooFinance::Error => e
  raise unless e.message.include?("No chart data found")
  # cross via USD: from->USD * USD->to
  base = provider.fetch_exchange_rates(from: from, to: "USD", start_date:, end_date:).data.index_by(&:date)
  provider.fetch_exchange_rates(from: "USD", to: to, start_date:, end_date:).data.filter_map do |r|
    b = base[r.date]
    b && Rate.new(date: r.date, from: from, to: to, rate: b.rate * r.rate)
  end
end

Prevention

When it happens

Trigger: Requesting a pair Yahoo Finance does not quote (exotic crosses like TRY/KRW), a date range entirely outside the pair's history, or a chart endpoint response whose chart.result is absent. Both direct and inverse fetches must fail for this to raise.

Common situations: Portfolio with currencies Yahoo doesn't cover as a direct pair; start_date before the pair's history begins; Yahoo silently changing chart response shape.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/47c86a1a3af068ec. Report an issue: GitHub.

Appendix: source

Thrown at app/models/provider/yahoo_finance.rb:181

    end
  end

  def fetch_exchange_rates(from:, to:, start_date:, end_date:)
    with_provider_response do
      validate_date_range!(start_date, end_date)
      # Return 1.0 rates if same currency
      if from == to
        generate_same_currency_rates(from, to, start_date, end_date)
      else
        cache_key = "exchange_rates_#{from}_#{to}_#{start_date}_#{end_date}"
        if cached_result = get_cached_result(cache_key)
          cached_result
        else
          # Try both direct and inverse currency pairs
          rates = fetch_currency_pair_data(from, to, start_date, end_date) ||
                  fetch_inverse_currency_pair_data(from, to, start_date, end_date)

          raise Error, "No chart data found for currency pair #{from}/#{to}" unless rates&.any?

          cache_result(cache_key, rates)
          rates
        end
      end
    rescue JSON::ParserError => e
      raise Error, "Invalid response format: #{e.message}"
    end
  end

  # ================================
  #           Securities
  # ================================

  def search_securities(symbol, country_code: nil, exchange_operating_mic: nil)
    with_provider_response do
      cache_key = "search_#{symbol}_#{country_code}_#{exchange_operating_mic}"
      if cached_result = get_cached_result(cache_key)

View on GitHub (pinned to e69894adb9)