{"record":{"id":"37f9b606a6597aca","repo":"we-promise/sure","slug":"no-exchange-rate-found-for-from-to-on-or-bef","errorCode":null,"errorMessage":"No exchange rate found for #{from}/#{to} on or before #{date}","messagePattern":"No exchange rate found for #(.+?)/#(.+?) on or before #(.+?)","errorType":"exception","errorClass":"Provider::YahooFinance::Error","httpStatus":null,"severity":"error","filePath":"app/models/provider/yahoo_finance.rb","lineNumber":156,"sourceCode":"\n          rates_response = fetch_exchange_rates(\n            from: from,\n            to: to,\n            start_date: start_date,\n            end_date: end_date\n          )\n\n          raise Error, \"Failed to fetch exchange rates: #{rates_response.error.message}\" unless rates_response.success?\n\n          rates = rates_response.data\n          if rates.length == 1\n            rates.first\n          else\n            # Find the exact date or the closest previous date\n            target_rate = rates.find { |r| r.date == date } ||\n                         rates.select { |r| r.date <= date }.max_by(&:date)\n\n            raise Error, \"No exchange rate found for #{from}/#{to} on or before #{date}\" unless target_rate\n\n            cache_result(cache_key, target_rate)\n            target_rate\n          end\n        end\n      end\n    end\n  end\n\n  def fetch_exchange_rates(from:, to:, start_date:, end_date:)\n    with_provider_response do\n      validate_date_range!(start_date, end_date)\n      # Return 1.0 rates if same currency\n      if from == to\n        generate_same_currency_rates(from, to, start_date, end_date)\n      else\n        cache_key = \"exchange_rates_#{from}_#{to}_#{start_date}_#{end_date}\"\n        if cached_result = get_cached_result(cache_key)","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/yahoo_finance.rb#L138-L174","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Confirm the requested date is in the past and within Yahoo's history for the pair — very old dates may simply be unavailable","For recent-date failures, retry with end_date = date + 1.day or use fetch_exchange_rates directly and pick the latest bar manually","Fall back to another FX provider for dates Yahoo can't serve","Normalize dates to the exchange's timezone before comparing"],"exampleFix":"// before\nrate = provider.fetch_exchange_rate(from: \"EUR\", to: \"USD\", date: Date.new(2010, 1, 4))\n\n// after\nbegin\n  rate = provider.fetch_exchange_rate(from: \"EUR\", to: \"USD\", date: date)\nrescue Provider::YahooFinance::Error => e\n  raise unless e.message.include?(\"No exchange rate found\")\n  resp = provider.fetch_exchange_rates(from: \"EUR\", to: \"USD\", start_date: date - 30.days, end_date: date + 1.day)\n  rate = resp.data.select { |r| r.date <= date }.max_by(&:date) or raise e\nend","handlingStrategy":"fallback","validationCode":"raise ArgumentError, \"date must be in the past\" if date > Date.current","typeGuard":null,"tryCatchPattern":"begin\n  provider.fetch_exchange_rate(from:, to:, date:)\nrescue Provider::YahooFinance::Error => e\n  raise unless e.message.include?(\"No exchange rate found\")\n  resp = provider.fetch_exchange_rates(from:, to:, start_date: date - 30.days, end_date: date + 1.day)\n  resp.data.select { |r| r.date <= date }.max_by(&:date) || fallback_provider.fetch_exchange_rate(from:, to:, date:)\nend","preventionTips":["Guard against future dates before requesting","For old transactions, try a wider window or a dedicated historical-FX provider","Handle timezone-sensitive date comparisons explicitly (normalize to UTC/exchange tz)"],"tags":["ruby","yahoo-finance","exchange-rate","date-out-of-range","no-data"],"backgroundTag":"no-historical-data-for-date","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}