{"record":{"id":"363324edf0d6dd49","repo":"Hmbown/CodeWhale","slug":"responses-json-body","errorCode":null,"errorMessage":"responses JSON body","messagePattern":"responses JSON body","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":7920,"sourceCode":"            offerings,\n        });\n\n        let mut client = codewhale_client(&server, \"openai/gpt-5.6\");\n        assert_eq!(client.wire_format, WireFormat::Responses);\n        client.retry.enabled = false;\n        let response = client\n            .create_message(minimal_zen_request(\"openai/gpt-5.6\"))\n            .await\n            .expect(\"Codewhale responses request should succeed\");\n\n        // Responses usage arrives on the terminal `response.completed` event —\n        // the dialect's equivalent of chat's `stream_options.include_usage`.\n        assert_eq!(response.usage.input_tokens, 3);\n        assert_eq!(response.usage.output_tokens, 1);\n        let requests = server.received_requests().await.expect(\"recorded request\");\n        assert_eq!(requests.len(), 1);\n        assert_codewhale_bearer(&requests[0]);\n        let body: Value = serde_json::from_slice(&requests[0].body).expect(\"responses JSON body\");\n        assert_eq!(\n            body.get(\"model\").and_then(Value::as_str),\n            Some(\"openai/gpt-5.6\")\n        );\n        assert!(body.get(\"input\").is_some(), \"Responses body: {body}\");\n        assert!(body.get(\"messages\").is_none(), \"Responses body: {body}\");\n\n        crate::provider_catalog_live::reset_cache_for_test();\n        crate::provider_lake::clear_live_snapshot();\n    }\n\n    fn opencode_zen_client(server: &MockServer, model: &str) -> CodewhaleClient {\n        let config = Config {\n            provider: Some(\"opencode-zen\".to_string()),\n            providers: Some(ProvidersConfig {\n                opencode_zen: ProviderConfig {\n                    api_key: Some(\"zen-test-key\".to_string()),\n                    base_url: Some(server.uri()),","sourceCodeStart":7902,"sourceCodeEnd":7938,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client.rs#L7902-L7938","documentation":"This `expect` panics when `serde_json::from_slice` cannot parse the recorded Responses request body, aborting with 'responses JSON body' at crates/tui/src/client.rs:7920. It verifies the client sent a valid JSON object to the `{base}/responses` endpoint; unparseable bytes mean the request serialization is broken or the recorded body is not what the client sent.","triggerScenarios":"Calling `serde_json::from_slice(&requests[0].body)` on a Responses-format request whose body is empty, truncated, SSE-encoded, or otherwise not a single JSON object.","commonSituations":"The Responses serializer emits an `input` array but a bug concatenates fragments; the mock records chunked/stream bytes as-is; a middleware rewrites or compresses the body before recording.","solutions":["Dump `String::from_utf8_lossy(&requests[0].body)` to inspect the raw payload","Confirm the client serializes the Responses payload as one JSON object with `model` and `input` fields","Rule out compression or chunked-recording artifacts in the mock","Fix the serializer so the body parses cleanly"],"exampleFix":"// before\nlet body: Value = serde_json::from_slice(&requests[0].body).expect(\"responses JSON body\");\n// after\nlet raw = String::from_utf8_lossy(&requests[0].body).to_string();\nlet body: Value = serde_json::from_str(&raw)\n    .unwrap_or_else(|e| panic!(\"responses JSON body ({e}): {raw}\"));","handlingStrategy":"validation","validationCode":"let raw = String::from_utf8_lossy(&bytes);\nlet v: serde_json::Value = serde_json::from_str(&raw)\n    .expect(\"responses request body must be valid JSON\");\nassert!(v.get(\"input\").is_some());","typeGuard":"fn as_json(bytes: &[u8]) -> Option<serde_json::Value> {\n    serde_json::from_slice(bytes).ok()\n}","tryCatchPattern":"let body: Value = serde_json::from_slice(&bytes)\n    .unwrap_or_else(|e| panic!(\"responses JSON body: {e}; raw: {}\", String::from_utf8_lossy(&bytes)));","preventionTips":["Assert body shape (input present, messages absent) alongside parseability","Dump raw bytes in the panic message for fast diagnosis","Keep streaming recorders byte-faithful (no recompression)"],"tags":["rust","json","http-test"],"backgroundTag":"json-parse-error","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}