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

Malformed Kraken API response: missing result

Error message

Malformed Kraken API response: missing result

What it means

Raised as Provider::Kraken::ApiError when a 2xx Kraken response is a valid Hash with an (empty) 'error' array but no 'result' key. Per Kraken's contract, a success is {error: [], result: ...}; reaching this guard means the call was neither an in-band error (those were classified into AuthenticationError/RateLimitError/NonceError/etc.) nor a normal success — the payload is structurally a success envelope missing its payload. This is the last of three shape guards in handle_response.

Source

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

      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)
      when /Invalid nonce/i
        NonceError.new(message)
      when /Permission denied|Invalid permissions/i
        PermissionError.new(message)
      when /Rate limit exceeded|Too many requests|limit exceeded|Throttled/i
        RateLimitError.new(message)
      when /otp|2fa|two.factor/i

View on GitHub (pinned to e69894adb9)

Solutions

  1. Reproduce with curl and inspect the full body: does 'result' exist at top level for this endpoint?
  2. Check whether a proxy or response-size limit is truncating large Kraken responses (balance/trade history payloads are big)
  3. Retry transiently — truncation artifacts usually resolve on a second attempt
  4. Fix test stubs to include a 'result' key with the expected payload shape
  5. If Kraken genuinely changed the envelope, update handle_response to read the new location of the payload

Example fix

# before (test stub) — passes error check, then fails 'missing result'
stub_request(:post, %r{api.kraken.com}).to_return(body: {error: []}.to_json)

# after — complete success envelope
stub_request(:post, %r{api.kraken.com}).to_return(body: {error: [], result: {"ZUSD": "100.0"}}.to_json)
Defensive patterns

Strategy: type-guard

Type guard

# Treat success only when the envelope is complete and result is present
def kraken_success?(parsed)
  parsed.is_a?(Hash) && parsed["error"].is_a?(Array) && parsed["error"].empty? && parsed.key?("result")
end

Try / catch

begin
  result = provider.get_ledgers
rescue Provider::Kraken::ApiError => e
  raise unless e.message.include?("missing result")
  # truncated payloads: retry once, then alert — indicates gateway truncation or contract change
  sleep 2
  retry
end

Prevention

When it happens

Trigger: Any Kraken request returning {error: [], ...} without 'result' — typically partial responses from a gateway, truncated JSON that still parsed, an upstream envelope change, or test stubs returning only {error: []}. Note some Kraken endpoints legitimately return result: {} (present but empty), which does NOT trigger this; the key itself must be absent.

Common situations: Truncated responses from proxies under load, Kraken API changes relocating the payload, incomplete test stubs, and HTTParty parsing edge cases where a cut-off body still yields a Hash without the later keys.

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/6375fd19387e1260. Report an issue: GitHub.