we-promise/sure · error · Provider::Kraken::ApiError
Malformed Kraken API response: missing error
Error message
Malformed Kraken API response: missing error
What it means
Raised as Provider::Kraken::ApiError when a 2xx Kraken response parses to a Hash but has no top-level 'error' key. Kraken's contract is strict: every response — success or failure — carries {error: [...], result: ...}. A missing 'error' key means the payload is not a genuine Kraken API response (gateway interference, version change, or truncated body), so the client refuses to trust it even if a 'result' is present.
Source
Thrown at app/models/provider/kraken.rb:131
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(", ")
case message
when /Invalid key|Invalid signature|Temporary lockout/i
AuthenticationError.new(message)View on GitHub (pinned to e69894adb9)
Solutions
- If seen in tests, fix stubs to include both keys: {"error" => [], "result" => ...}
- curl the live endpoint and verify the body actually contains an 'error' key on success (it is [] on success)
- Check for proxies/middleboxes between the app and api.kraken.com that could rewrite JSON
- Verify BASE_URL has not been overridden to a non-Kraken host in the environment
- Check Kraken changelog if live responses genuinely dropped the key; the guard may need updating alongside
Example fix
# before (test stub) — triggers 'missing error'
stub_request(:post, %r{api.kraken.com}).to_return(body: {result: {ZUSD: 1}}.to_json)
# after — stub matches Kraken's real envelope
stub_request(:post, %r{api.kraken.com}).to_return(body: {error: [], result: {ZUSD: 1}}.to_json) Defensive patterns
Strategy: type-guard
Type guard
# Full envelope guard: both keys required before trusting a Kraken body
def valid_kraken_envelope?(parsed)
parsed.is_a?(Hash) && parsed.key?("error") && parsed.key?("result") && parsed["error"].is_a?(Array)
end Try / catch
begin
result = provider.get_api_key_info
rescue Provider::Kraken::ApiError => e
raise unless e.message.include?("missing error")
notify_ops("Kraken envelope drift: response missing 'error' key — check for proxies/upstream changes")
raise
end Prevention
- Always stub BOTH 'error' and 'result' keys in test fixtures
- Keep proxies transparent — any body rewriting breaks Kraken's strict envelope contract
- Verify BASE_URL points at api.kraken.com (no mirror) when this fires
- Monitor for upstream contract changes via a cheap scheduled ping to a public endpoint
When it happens
Trigger: Any Kraken request whose 2xx JSON body lacks the 'error' field — e.g. a proxy injecting its own JSON object, Kraken shipping a new envelope without the key, or a mock/stub in tests returning {result: ...} without the error array. Fires after the Hash check but before in-band error classification.
Common situations: Test suites stubbing Kraken responses incompletely (missing the 'error' key), API gateways rewriting responses, upstream Kraken API contract changes, and responses from a different service reached via a wrong BASE_URL override.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Malformed Kraken API response: missing result
- Malformed Kraken API response
- Unexpected response format from search API
- Unexpected response format from EOD API
- Unexpected Frankfurter response shape
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/00ad33ffcb46ae69.
Report an issue: GitHub.