Hmbown/CodeWhale · error

chat JSON body

Error message

chat JSON body

What it means

This is a Rust `expect` panic on `serde_json::from_slice`: the test sent a chat request to a mock server, read the recorded request body back, and JSON deserialization failed, so the process aborts with message 'chat JSON body'. The library/test throws it because a chat-completions request body that should be valid JSON was not parseable, meaning the client serialized something malformed or the recorded bytes were truncated/corrupt.

Solutions

  1. Print `String::from_utf8_lossy(&requests[0].body)` before parsing to see the actual bytes
  2. Confirm the client is using the Chat Completions wire format, not Responses or AnthropicMessages, for this endpoint
  3. Check that no compression/middleware transformed the recorded body
  4. Fix the serializer so the body is a single valid JSON object

Example fix

// before
let body: Value = serde_json::from_slice(&requests[0].body).expect("chat JSON body");
// after
let raw = String::from_utf8_lossy(&requests[0].body).to_string();
let body: Value = serde_json::from_str(&raw)
    .unwrap_or_else(|e| panic!("chat JSON body ({e}): {raw}"));
Defensive patterns

Strategy: validation

Validate before calling

let raw = String::from_utf8_lossy(&body_bytes);
if serde_json::from_str::<serde_json::Value>(&raw).is_err() {
    panic!("recorded body is not valid JSON: {raw}");
}

Type guard

fn is_json_object(bytes: &[u8]) -> Option<serde_json::Value> {
    serde_json::from_slice(bytes).ok()
}

Try / catch

let body: Value = serde_json::from_slice(&bytes)
    .unwrap_or_else(|e| panic!("chat JSON body: {e}; raw: {}", String::from_utf8_lossy(&bytes)));

Prevention

When it happens

Trigger: Calling `serde_json::from_slice(&requests[0].body)` in crates/tui/src/client.rs:7809 when the client under test produced a body that is not valid JSON — e.g. a serializer bug, partial write, or binary/gzip bytes recorded instead of raw JSON.

Common situations: A wire-format change makes the client emit non-JSON (e.g. SSE lines or form-encoded data) on the chat endpoint; a mock server records compressed bytes; a test asserts on a request that was never fully received.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/895bc3c06af4a515. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/client.rs:7809

                    "finish_reason": "stop"
                }],
                "usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2}
            })))
            .expect(1)
            .mount(&server)
            .await;

        let client = codewhale_client(&server, "deepseek/deepseek-v4-pro");
        assert_eq!(client.wire_format, WireFormat::ChatCompletions);
        client
            .create_message(minimal_zen_request("deepseek/deepseek-v4-pro"))
            .await
            .expect("Codewhale chat request should succeed");

        let requests = server.received_requests().await.expect("recorded request");
        assert_eq!(requests.len(), 1);
        assert_codewhale_bearer(&requests[0]);
        let body: Value = serde_json::from_slice(&requests[0].body).expect("chat JSON body");
        // Model ids reach the account API exactly as its catalog returns them.
        assert_eq!(
            body.get("model").and_then(Value::as_str),
            Some("deepseek/deepseek-v4-pro")
        );
    }

    #[tokio::test]
    async fn codewhale_messages_request_carries_the_account_bearer_not_x_api_key() {
        let server = MockServer::start().await;
        Mock::given(method("POST"))
            .and(path("/v1/messages"))
            .respond_with(ResponseTemplate::new(200).set_body_json(json!({
                "id": "msg_cw",
                "type": "message",
                "role": "assistant",
                "content": [{"type": "text", "text": "ok"}],
                "model": "anthropic/claude-sonnet-5",

View on GitHub (pinned to 73e0f67d83)