{"record":{"id":"533ecf08d5847e74","repo":"Hmbown/CodeWhale","slug":"unrecognized-sse-event-error","errorCode":null,"errorMessage":"unrecognized SSE event: {error}","messagePattern":"unrecognized SSE event: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client/anthropic.rs","lineNumber":901,"sourceCode":"    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    };\n\n    match value.get(\"type\").and_then(Value::as_str) {\n        Some(\"message_start\") => {\n            if let Some(usage) = value\n                .get_mut(\"message\")","sourceCodeStart":883,"sourceCodeEnd":919,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client/anthropic.rs#L883-L919","documentation":"Raised by `convert_anthropic_sse_data` when the SSE data line is valid JSON but its `type` does not match any event type the client knows how to decode (the typed decode failed and the value is not an unknown-but-tolerable type, or there is no `type` field). This is the legacy error preserved for decode failures of recognized/shapeless events.","triggerScenarios":"A JSON SSE frame whose `type` string is a known Anthropic type but whose body fails typed deserialization (missing/wrongly-typed fields), or a frame with no `type` field at all.","commonSituations":"Anthropic API version drift introducing new required fields; a broken fixture; a middleware injecting extra JSON that shifts field types (e.g. numbers as strings).","solutions":["Check the `error` text in the message for the underlying serde deserialization failure","Pin or update the Anthropic API version header so client and wire format match","Validate the event JSON against the expected event schema (e.g. `content_block_start` requires `index` and `content_block`)","If a new upstream event type appears, add it to `is_known_sse_type` so it is silently tolerated"],"exampleFix":"// before\n{\"type\":\"content_block_delta\",\"delta\":{\"text\":\"hi\"}}\n// after\ndelta must carry its discriminator: {\"type\":\"content_block_delta\",\"delta\":{\"type\":\"text_delta\",\"text\":\"hi\"}}","handlingStrategy":"try-catch","validationCode":"fn event_shape_ok(v: &serde_json::Value) -> bool {\n    v.get(\"type\").and_then(|t| t.as_str()).is_some()\n}","typeGuard":"fn has_event_type(v: &serde_json::Value) -> Option<&str> {\n    v.get(\"type\").and_then(|t| t.as_str())\n}","tryCatchPattern":"match result {\n    Ok(event) => handle(event),\n    Err(e) if e.to_string().contains(\"unrecognized SSE event\") => {\n        // inspect the serde sub-error embedded in the message\n        log_raw_event(&raw); retry_or_fail(e);\n    }\n    Err(e) => retry_or_fail(e),\n}","preventionTips":["Pin the Anthropic API version header (`anthropic-version`)","Add new upstream event types to `is_known_sse_type` promptly","Validate fixtures against current Anthropic event schemas","Watch provider changelogs for required-field changes"],"tags":["sse","anthropic","streaming","deserialization"],"backgroundTag":"unexpected-api-response-shape","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"}