{"record":{"id":"e8c2aaa541265dc9","repo":"Hmbown/CodeWhale","slug":"invalid-sse-json-e","errorCode":null,"errorMessage":"invalid SSE JSON: {e}","messagePattern":"invalid SSE JSON: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client/anthropic.rs","lineNumber":896,"sourceCode":"    }\n    let usage_event = matches!(\n        serde_json::from_str::<SseTagPeek>(trimmed).map(|peek| peek.r#type),\n        Ok(Some(\"message_start\" | \"message_delta\"))\n    );\n    if usage_event {\n        return convert_anthropic_sse_usage_event(trimmed);\n    }\n    match serde_json::from_str::<StreamEvent>(trimmed) {\n        // Local-only receipt: the legacy path ignored it (not a provider\n        // type), so it stays ignored rather than decoding.\n        Ok(StreamEvent::ToolProjectionWarning { .. }) => None,\n        Ok(event) => Some(Ok(event)),\n        Err(error) => {\n            // Cold path, reached only when direct decode fails: invalid JSON\n            // and unknown types keep their exact legacy outcomes.\n            let value: Value = match serde_json::from_str(trimmed) {\n                Ok(value) => value,\n                Err(e) => return Some(Err(anyhow::anyhow!(\"invalid SSE JSON: {e}\"))),\n            };\n            match value.get(\"type\").and_then(Value::as_str) {\n                // Tolerate unknown event types (e.g. future additions) silently.\n                Some(known) if !is_known_sse_type(known) => None,\n                _ => Some(Err(anyhow::anyhow!(\"unrecognized SSE event: {error}\"))),\n            }\n        }\n    }\n}\n\n/// Legacy `Value` path for `message_start`/`message_delta`: the usage\n/// rewrite reads wire fields the normalized [`Usage`] cannot represent, so\n/// these two events normalize before decoding, exactly as before.\nfn convert_anthropic_sse_usage_event(trimmed: &str) -> Option<Result<StreamEvent>> {\n    let mut value: Value = match serde_json::from_str(trimmed) {\n        Ok(value) => value,\n        Err(e) => return Some(Err(anyhow::anyhow!(\"invalid SSE JSON: {e}\"))),\n    };","sourceCodeStart":878,"sourceCodeEnd":914,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client/anthropic.rs#L878-L914","documentation":"This error is raised by `convert_anthropic_sse_data` in the Anthropic SSE client when a raw `data:` line from the stream cannot be parsed as JSON by the fallback legacy path. The function first tries a typed decode; on failure it attempts `serde_json::from_str`, and if even that fails, the line is not valid JSON at all, so the stream yields a hard error and terminates.","triggerScenarios":"An Anthropic SSE `data:` frame contains malformed JSON — e.g. truncated frames from a proxy buffering bug, a mock/test fixture with bad JSON, or a non-Anthropic endpoint behind an Anthropic-compatible base URL emitting non-JSON payloads.","commonSituations":"Corporate proxies or gateways that mangle chunked responses; pointing the client at an OpenAI-style or self-hosted endpoint that emits malformed lines; hand-written SSE fixtures in tests with typos in the JSON.","solutions":["Log the raw `data:` line before parsing to inspect the exact malformed payload","Check whether a proxy/gateway is truncating or rewriting SSE chunks and bypass or fix it","Verify the base URL points at the real Anthropic Messages API","Fix the malformed fixture if this comes from a test fixture"],"exampleFix":"// before: fixture with truncated JSON\n{\"type\":\"content_block_delta\",\"delta\":{\"type\":\"text_delta\",\"text\":\"hi\"\n// after: complete JSON object per SSE data line\n{\"type\":\"content_block_delta\",\"delta\":{\"type\":\"text_delta\",\"text\":\"hi\"}}","handlingStrategy":"try-catch","validationCode":"// pre-validate a fixture line before feeding the SSE parser\nfn is_valid_json_line(line: &str) -> bool {\n    serde_json::from_str::<serde_json::Value>(line.trim()).is_ok()\n}","typeGuard":"fn parse_sse_json(line: &str) -> Option<serde_json::Value> {\n    serde_json::from_str(line.trim()).ok()\n}","tryCatchPattern":"match result {\n    Ok(event) => handle(event),\n    Err(e) if e.to_string().contains(\"invalid SSE JSON\") => {\n        log_raw_line(&raw); // capture payload for diagnosis\n        fail_stream(e);\n    }\n    Err(e) => fail_stream(e),\n}","preventionTips":["Log raw SSE lines at debug level before parsing","Test SSE fixtures with a JSON schema validator","Bypass proxies when diagnosing to rule out chunk mangling","Keep base URL pointed at the genuine Anthropic Messages API"],"tags":["sse","json","streaming","anthropic"],"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"}