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
- Print `String::from_utf8_lossy(&requests[0].body)` before parsing to see the actual bytes
- Confirm the client is using the Chat Completions wire format, not Responses or AnthropicMessages, for this endpoint
- Check that no compression/middleware transformed the recorded body
- 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
- Inspect raw request bytes before asserting on parsed JSON
- Keep one wire format per test so serializer mismatches are obvious
- Disable compression middleware in mock servers used for body assertions
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)