{"record":{"id":"ffb1afeca0d466a0","repo":"we-promise/sure","slug":"api-error-code-error-code-error-message","errorCode":null,"errorMessage":"API error (code: #{error_code}): #{error_message}","messagePattern":"API error \\(code: #(.+?)\\): #(.+?)","errorType":"exception","errorClass":"Provider::TwelveData::InvalidExchangeRateError","httpStatus":null,"severity":"error","filePath":"app/models/provider/twelve_data.rb","lineNumber":114,"sourceCode":"        Rails.logger.info(\"#{self.class.name}: Currency pair #{from}/#{to} not available, fetching via time_series/cross API\")\n        throttle_request(credits: 5)\n        response = client.get(\"#{base_url}/time_series/cross\") do |req|\n          req.params[\"base\"] = from\n          req.params[\"quote\"] = to\n          req.params[\"start_date\"] = start_date.to_s\n          req.params[\"end_date\"] = end_date.to_s\n          req.params[\"interval\"] = \"1day\"\n        end\n\n        parsed = JSON.parse(response.body)\n        check_api_error!(parsed)\n        data = parsed.dig(\"values\")\n      end\n\n      if data.nil?\n        error_message = parsed.dig(\"message\") || \"No data returned\"\n        error_code = parsed.dig(\"code\") || \"unknown\"\n        raise InvalidExchangeRateError, \"API error (code: #{error_code}): #{error_message}\"\n      end\n\n      data.map do |resp|\n        rate = resp.dig(\"close\")\n        date = resp.dig(\"datetime\")\n        if rate.nil? || rate.to_f <= 0\n          Rails.logger.warn(\"#{self.class.name} returned invalid rate data for pair from: #{from} to: #{to} on: #{date}.  Rate data: #{rate.inspect}\")\n          next\n        end\n\n        Rate.new(date: date.to_date, from:, to:, rate:)\n      end.compact\n    end\n  end\n\n  # ================================\n  #           Securities\n  # ================================","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/twelve_data.rb#L96-L132","documentation":"Provider::TwelveData::InvalidExchangeRateError raised in fetch_exchange_rates when the response body has no \"values\" array after both attempts: the /time_series endpoint (direct pair, 1 credit) and the /time_series/cross fallback (5 credits). check_api_error! already passed, so the API responded successfully but with no data payload for the pair/date range.","triggerScenarios":"Fetching exchange rates for a pair Twelve Data has no time series for (both direct and cross listings empty), a date range where no data exists (weekend/holiday-only range), or an unsupported/exotic currency code that returns JSON without a values key.","commonSituations":"Syncing an account with an exotic currency (e.g. TRY/KRW crosses); requesting a single weekend date; a typo'd currency code that silently yields an empty response; free-plan limits returning empty bodies instead of explicit errors.","solutions":["Verify the currency pair with a direct API call (curl 'https://api.twelvedata.com/time_series?symbol=EUR/USD&apikey=...') to see whether values are returned at all","Widen the date range — single-day or weekend-only windows commonly come back empty","Check the error_code/message embedded in the message: code 429 means rate limit (raise came after throttle), other codes indicate plan/pair restrictions","Fall back to another exchange-rate provider for pairs Twelve Data does not cover"],"exampleFix":"// before\nrates = provider.fetch_exchange_rates(from: \"TRY\", to: \"KRW\", start_date: d, end_date: d)\n\n// after\nresponse = provider.fetch_exchange_rates(from: \"TRY\", to: \"KRW\", start_date: d - 7.days, end_date: d)\nif response.error.present?\n  Rails.logger.warn(\"TwelveData FX failed: #{response.error.message}\")\n  response = fallback_provider.fetch_exchange_rates(from: \"TRY\", to: \"KRW\", start_date: d - 7.days, end_date: d)\nend\nrates = response.data","handlingStrategy":"fallback","validationCode":"response = provider.fetch_exchange_rates(from:, to:, start_date: date - 7.days, end_date: date)\nif response.error.present?\n  # use fallback provider rather than raising into the caller\nend","typeGuard":null,"tryCatchPattern":"begin\n  provider.fetch_exchange_rates(from:, to:, start_date:, end_date:)\nrescue Provider::TwelveData::InvalidExchangeRateError => e\n  Rails.logger.warn(\"TwelveData FX unavailable for #{from}/#{to}: #{e.message}\")\n  fallback_provider.fetch_exchange_rates(from:, to:, start_date:, end_date:)\nend","preventionTips":["Widen date ranges — single-day and weekend-only windows come back empty","Pre-check pair support with a cached capability list or cheap /exchange_rate call before time_series","Keep a second FX provider wired for pairs Twelve Data doesn't cover"],"tags":["ruby","twelvedata","exchange-rate","empty-response","api"],"backgroundTag":"provider-empty-response","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}