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

No exchange rate found for #{from}/#{to} on or before #{date

Error message

No exchange rate found for #{from}/#{to} on or before #{date}

What it means

Provider::YahooFinance::Error raised in fetch_exchange_rate when the fetched 10-day window returned rates but none match the target date and none are on or before it. Every returned bar is later than the requested date, so no 'exact or closest previous' rate exists.

Source

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

          rates_response = fetch_exchange_rates(
            from: from,
            to: to,
            start_date: start_date,
            end_date: end_date
          )

          raise Error, "Failed to fetch exchange rates: #{rates_response.error.message}" unless rates_response.success?

          rates = rates_response.data
          if rates.length == 1
            rates.first
          else
            # Find the exact date or the closest previous date
            target_rate = rates.find { |r| r.date == date } ||
                         rates.select { |r| r.date <= date }.max_by(&:date)

            raise Error, "No exchange rate found for #{from}/#{to} on or before #{date}" unless target_rate

            cache_result(cache_key, target_rate)
            target_rate
          end
        end
      end
    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)

View on GitHub (pinned to e69894adb9)

Solutions

  1. Confirm the requested date is in the past and within Yahoo's history for the pair — very old dates may simply be unavailable
  2. For recent-date failures, retry with end_date = date + 1.day or use fetch_exchange_rates directly and pick the latest bar manually
  3. Fall back to another FX provider for dates Yahoo can't serve
  4. Normalize dates to the exchange's timezone before comparing

Example fix

// before
rate = provider.fetch_exchange_rate(from: "EUR", to: "USD", date: Date.new(2010, 1, 4))

// after
begin
  rate = provider.fetch_exchange_rate(from: "EUR", to: "USD", date: date)
rescue Provider::YahooFinance::Error => e
  raise unless e.message.include?("No exchange rate found")
  resp = provider.fetch_exchange_rates(from: "EUR", to: "USD", start_date: date - 30.days, end_date: date + 1.day)
  rate = resp.data.select { |r| r.date <= date }.max_by(&:date) or raise e
end
Defensive patterns

Strategy: fallback

Validate before calling

raise ArgumentError, "date must be in the past" if date > Date.current

Try / catch

begin
  provider.fetch_exchange_rate(from:, to:, date:)
rescue Provider::YahooFinance::Error => e
  raise unless e.message.include?("No exchange rate found")
  resp = provider.fetch_exchange_rates(from:, to:, start_date: date - 30.days, end_date: date + 1.day)
  resp.data.select { |r| r.date <= date }.max_by(&:date) || fallback_provider.fetch_exchange_rate(from:, to:, date:)
end

Prevention

When it happens

Trigger: Requesting a rate for a date older than Yahoo's available history for the pair (e.g. a 15-year-old transaction on a newly listed currency), a future date, or a pair whose 10-day lookback window starts after the target date. Timezone shifts can also make returned dates parse as after the requested date.

Common situations: Importing very old transactions whose FX history Yahoo no longer serves; requesting today/forward dates when Yahoo's latest bar is yesterday; exotic pairs with sparse history.

Related errors


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