we-promise/sure · error · Provider::Frankfurter::Error
Invalid date in Frankfurter response: #{e.message}
Error message
Invalid date in Frankfurter response: #{e.message} What it means
Raised as Provider::Frankfurter::Error when the 'date' field of a successful /rate/<from>/<to> response cannot be parsed by Date.parse (a Date::Error). Frankfurter carries forward weekend/holiday rates, so the returned date may differ from the requested one but is expected to always be a valid ISO date; if the service ever returns null, an empty string, or a non-date string, this guard converts the Ruby parse failure into a provider error naming the underlying message.
Source
Thrown at app/models/provider/frankfurter.rb:59
# GET /rate/{base}/{quote}?date=... -> { date:, base:, quote:, rate: }.
# Frankfurter carries forward weekends/holidays itself, so the returned
# date may differ from the requested one but is never simply missing.
def fetch_exchange_rate(from:, to:, date:)
from = sanitize_currency(from)
to = sanitize_currency(to)
with_provider_response do
if from == to
Rate.new(date: date, from: from, to: to, rate: 1.0)
else
body = get_json("/rate/#{from}/#{to}", "date" => date.to_s)
raise Error, "Unexpected Frankfurter response shape" unless body.is_a?(Hash) && body["rate"]
begin
parsed_date = Date.parse(body["date"].to_s)
rescue Date::Error => e
raise Error, "Invalid date in Frankfurter response: #{e.message}"
end
Rate.new(date: parsed_date, from: from, to: to, rate: body["rate"].to_f)
end
end
end
def fetch_exchange_rates(from:, to:, start_date:, end_date:)
from = sanitize_currency(from)
to = sanitize_currency(to)
with_provider_response do
if from == to
generate_same_currency_rates(from, to, start_date, end_date)
else
exchange_rates(from, to, start_date, end_date)
end
endView on GitHub (pinned to e69894adb9)
Solutions
- curl the exact URL with the failing date and inspect the 'date' field: curl "https://api.frankfurter.dev/v2/rate/USD/EUR?date=1990-01-01"
- If requesting very old dates, clamp start dates to the provider's history window (ECB data begins 1999-01-04)
- If a mirror returns null dates, switch FRANKFURTER_URL to the official api.frankfurter.dev host
- Validate the requested date is a real calendar date before calling (catch typos like 2025-13-01 earlier)
- If upstream changed the date format, parse the new format explicitly with Date.strptime(body['date'], '%Y-%m-%d') and a clear error
Example fix
# before: any Date.parse failure is fatal parsed_date = Date.parse(body["date"].to_s) # after: reject blank early with a targeted message, then parse strictly raw = body["date"].to_s raise Error, "Frankfurter returned no date for rate" if raw.blank? parsed_date = Date.strptime(raw, "%Y-%m-%d")
Defensive patterns
Strategy: validation
Validate before calling
# Clamp requested dates to Frankfurter's history window and sanity-check format MIN_FRANKFURTER_DATE = Date.new(1999, 1, 4) def valid_rate_request_date?(date) date.is_a?(Date) && date >= MIN_FRANKFURTER_DATE && date <= Date.current end
Type guard
# Strict parse of the expected ISO date field
def parse_frankfurter_date(value)
return nil unless value.is_a?(String) && value.match?(\A\d{4}-\d{2}-\d{2}\z)
Date.strptime(value, "%Y-%m-%d")
end Try / catch
begin
rate = provider.fetch_exchange_rate(from: from, to: to, date: date)
rescue Provider::Frankfurter::Error => e
raise unless e.message.include?("Invalid date")
Rails.logger.warn("Frankfurter bad date for #{from}/#{to} @ #{date}: #{e.message}")
nil
end Prevention
- Clamp FX date requests to the provider's history window (ECB data from 1999-01-04)
- Parse response dates strictly with Date.strptime('%Y-%m-%d') instead of Date.parse
- Validate user-supplied dates before passing them through to provider calls
- Watch for upstream format changes by logging unparseable values verbatim
When it happens
Trigger: Calling fetch_exchange_rate where body['date'] is missing, empty, null, or in an unparseable format — e.g. an API change returning a timestamp instead of 'YYYY-MM-DD', a mirror returning {'date': null} for dates before its data start, or a response envelope where 'date' holds an object.
Common situations: Requesting rates before Frankfurter's earliest available date (ECB history starts 1999), mirrors with partial datasets, upstream format regressions, and Date.parse edge cases like ambiguous strings. Note Ruby's Date.parse raising Date::Error (not ArgumentError) requires Ruby >= 3.2 style handling — this rescue targets that.
Related errors
- Unexpected Frankfurter response shape
- Unexpected Frankfurter response shape (expected an array)
- Frankfurter currencies endpoint returned no data
- Invalid Frankfurter response: #{e.message}
- unknown
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/9cfaf849ae02fcd0.
Report an issue: GitHub.