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

Malformed Kraken API response

Error message

Malformed Kraken API response

What it means

Raised as Provider::Kraken::ApiError when a successful (2xx) Kraken response body does not parse to a Hash. Kraken's REST API always returns a JSON object {error: [...], result: ...}; a non-Hash parsed body means the payload was an array, scalar, or an empty body (HTTParty's parsed_response can be nil when the body is blank). This guard runs after the status check and before Kraken's in-band error handling.

Source

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

    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"]
    end

    def classified_error(errors)
      message = errors.join(", ")

View on GitHub (pinned to e69894adb9)

Solutions

  1. Reproduce the exact call with curl -i and inspect Content-Length/Content-Type of the response
  2. Check Kraken status page — truncated/empty 2xx bodies often coincide with incidents
  3. Retry once; truncated responses are usually transient network artifacts
  4. If a proxy is in path, bypass it to confirm the raw Kraken response
  5. Consider treating nil parsed_response (blank body) as retryable distinctly from wrong-shape bodies

Example fix

# before: nil parsed_response (empty body) hits the same opaque guard
parsed = response.parsed_response
raise ApiError, "Malformed Kraken API response" unless parsed.is_a?(Hash)

# after: distinguish empty (retryable) from wrong-shape (fatal)
parsed = response.parsed_response
if parsed.nil?
  raise ApiError, "Kraken returned an empty body (HTTP #{response.code})"
end
raise ApiError, "Malformed Kraken API response: #{parsed.class}" unless parsed.is_a?(Hash)
Defensive patterns

Strategy: retry

Type guard

# Narrow a parsed Kraken response before use
module KrakenGuard
  Envelope = Struct.new(:error, :result)

  def self.parse(response)
    parsed = response.parsed_response
    return nil unless parsed.is_a?(Hash) && parsed.key?("error") && parsed.key?("result")
    Envelope.new(Array(parsed["error"]), parsed["result"])
  end
end

Try / catch

begin
  result = provider.get_trades_history
rescue Provider::Kraken::ApiError => e
  raise unless e.message == "Malformed Kraken API response"
  # empty/truncated bodies are usually transient — single retry with backoff
  sleep 2
  retry
end

Prevention

When it happens

Trigger: Any Kraken call where the body is empty (connection closed mid-response, 204) or JSON that is not an object — e.g. a proxy serving a bare JSON array/string, or an upstream change in the response envelope. parsed_response returning nil on blank bodies is the most common concrete case.

Common situations: Load balancers or CDNs truncating responses, Kraken maintenance pages returning non-JSON-Object payloads with 2xx, HTTParty version changes in how blank bodies parse, and corporate proxies rewriting bodies.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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