we-promise/sure · error · Provider::Kraken::ApiError

Kraken API request failed: #{response.code}

Error message

Kraken API request failed: #{response.code}

What it means

Raised as Provider::Kraken::ApiError when the Kraken REST API returns an HTTP status outside 200-299 for a signed private or public request. Kraken normally signals errors in-band with HTTP 200 + an 'error' array, so a non-2xx status indicates a transport/protocol-level problem: 4xx for malformed requests/auth (403 signature, 400 bad payload) or 5xx for Kraken outages. The HTTP code is embedded in the message verbatim.

Source

Thrown at app/models/provider/kraken.rb:123

      {
        "API-Key" => api_key,
        "API-Sign" => sign(path, params)
      }
    end

    def sign(path, params)
      encoded_payload = URI.encode_www_form(params)
      nonce = params.fetch("nonce").to_s
      digest = OpenSSL::Digest::SHA256.digest(nonce + encoded_payload)
      hmac = OpenSSL::HMAC.digest("sha512", Base64.decode64(api_secret), path + digest)
      Base64.strict_encode64(hmac)
    end

    def handle_response(response)
      parsed = response.parsed_response

      unless response.code.between?(200, 299)
        raise ApiError, "Kraken API request failed: #{response.code}"
      end

      unless parsed.is_a?(Hash)
        raise ApiError, "Malformed Kraken API response"
      end

      unless parsed.key?("error")
        raise ApiError, "Malformed Kraken API response: missing error"
      end

      errors = Array(parsed["error"]).reject(&:blank?)
      raise classified_error(errors) if errors.any?

      unless parsed.key?("result")
        raise ApiError, "Malformed Kraken API response: missing result"
      end

      parsed["result"]

View on GitHub (pinned to e69894adb9)

Solutions

  1. Map the embedded code: 403 → check API key permissions and IP whitelist in Kraken settings; 418/429 → slow down (Kraken rate-limits per key); 5xx → check Kraken status and retry later
  2. Verify the server clock is NTP-synced — signature validation depends on the nonce/timestamp
  3. Confirm the API key has the required permissions (Query Funds, Query Open/Closed Orders, etc.) for the endpoint being called
  4. If IP-whitelisted, add the egress IP of the app server to the key's whitelist
  5. Honor Retry-After on 429 and add exponential backoff around private_post calls

Example fix

# before: caller retries blindly on any ApiError
rescue Provider::Kraken::ApiError
  retry
end

# after: branch on the embedded HTTP code
rescue Provider::Kraken::ApiError => e
  code = e.message[/\d{3}/]
  raise if code.in?(["403", "400"])           # config errors: fix, don't retry
  sleep(e.message.include?("429") ? 5 : 1)    # rate limit / transient 5xx
  retry
end
Defensive patterns

Strategy: try-catch

Validate before calling

# Verify key permissions/clock before long syncs (cheap public call also works)
# Kraken private calls need Query Funds + Query Ledger/Orders permissions and NTP-synced clock.
# Pre-flight clock check:
drift = (Process.clock_gettime(Process::CLOCK_REALTIME) - Time.now.to_f).abs
raise "Server clock drift too high for Kraken signing" if drift > 30

Try / catch

begin
  result = provider.get_extended_balance
rescue Provider::Kraken::ApiError => e
  code = e.message.scan(/\b(\d{3})\b/).last
  case code
  when "403" then disconnect_kraken("Auth/IP restriction: #{e.message}")
  when "418", "429" then reschedule_with_backoff
  when nil then raise
  else raise if code.start_with?("5").then { |five| !five } # 5xx → retry, else raise
  end
end

Prevention

When it happens

Trigger: Any private_post/public_get call (BalanceEx, TradesHistory, Ledgers, Assets...) receiving e.g. 403 (bad API key/signature/IP restriction), 418/429 (rate-limited or banned at HTTP layer), 400 (malformed request), or 5xx (Kraken incident). Distinguishable from Kraken's in-band errors, which are classified separately into AuthenticationError/RateLimitError/NonceError/OTPRequiredError.

Common situations: API key without 'Query Funds'/'Query Ledger' permissions enabled, IP whitelist on the Kraken key excluding the server, clock skew breaking signatures, Kraken 5xx during maintenance windows, and Cloudflare-level blocks (403/429) when request volume is too high from one IP.

Related errors


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