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

API error (code: #{parsed["code"]}): #{parsed["message"] ||

Error message

API error (code: #{parsed["code"]}): #{parsed["message"] || "Unknown error"}

What it means

Provider::TwelveData::Error raised by check_api_error! for any non-429 code field in the response body. Twelve Data returns errors as JSON with a numeric code and message (400 bad parameters, 401 invalid key, 403 plan restrictions, 4xx/5xx-style codes) even with HTTP 200, and this guard converts them into a typed provider error.

Source

Thrown at app/models/provider/twelve_data.rb:332

      @last_request_time = Time.current
    end

    def min_request_interval
      ENV.fetch("TWELVE_DATA_MIN_REQUEST_INTERVAL", MIN_REQUEST_INTERVAL).to_f
    end

    def max_requests_per_minute
      ENV.fetch("TWELVE_DATA_MAX_REQUESTS_PER_MINUTE", 7).to_i
    end

    def check_api_error!(parsed)
      return unless parsed.is_a?(Hash) && parsed["code"].present?

      if parsed["code"] == 429
        raise RateLimitError, parsed["message"] || "Rate limit exceeded"
      end

      raise Error, "API error (code: #{parsed["code"]}): #{parsed["message"] || "Unknown error"}"
    end

    def default_error_transformer(error)
      case error
      when RateLimitError
        error
      when Faraday::TooManyRequestsError
        RateLimitError.new("TwelveData rate limit exceeded", details: error.response&.dig(:body))
      when Faraday::Error
        self.class::Error.new(error.message, details: error.response&.dig(:body))
      else
        self.class::Error.new(error.message)
      end
    end
end

View on GitHub (pinned to e69894adb9)

Solutions

  1. Read the code and message in the error string — code 401 means the apikey header is wrong, 400 means fix params, plan names mean upgrade
  2. Test the same request with curl and the same apikey to confirm the account/plan can access the endpoint
  3. For plan-upgrade messages, check Provider::TwelveData.plan_upgrade_required?(e.message) / extract_required_plan(e.message) and surface a plan requirement to the user
  4. Regenerate the API key in the Twelve Data dashboard if 401 persists

Example fix

// before
begin
  provider.fetch_security_info(symbol: "AAPL", exchange_operating_mic: "XNAS")
rescue Provider::TwelveData::Error
  raise
end

// after
begin
  provider.fetch_security_info(symbol: "AAPL", exchange_operating_mic: "XNAS")
rescue Provider::TwelveData::Error => e
  if Provider::TwelveData.plan_upgrade_required?(e.message)
    required = Provider::TwelveData.extract_required_plan(e.message)
    notify_user("Twelve Data #{required} plan required for this endpoint")
  else
    raise
  end
end
Defensive patterns

Strategy: try-catch

Validate before calling

unless provider.healthy?
  # Twelve Data /api_usage failed — skip the sync rather than eating body-coded errors
end

Try / catch

begin
  provider.fetch_security_info(symbol:, exchange_operating_mic:)
rescue Provider::TwelveData::Error => e
  if Provider::TwelveData.plan_upgrade_required?(e.message)
    handle_plan_required(Provider::TwelveData.extract_required_plan(e.message))
  else
    raise
  end
end

Prevention

When it happens

Trigger: Invalid or revoked apikey (code 401), endpoint not in your plan (grow/grow/pro codes with 'available starting with PRO' messages — see PLAN_UPGRADE_PATTERN), malformed symbol or date params (code 400), or unsupported parameter combinations.

Common situations: API key expired or mistyped; free plan hitting an endpoint that requires a paid tier; passing mic_code/symbol combos the endpoint rejects; environment differences where the staging key belongs to a different plan.

Related errors


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