feat: parse unknown system-message subtypes into a Generic struct#54
Conversation
A newer CLI emits system-message subtypes this SDK version does not model yet
(e.g. thinking_tokens). The strict subtype dispatch returned
{:error, {:unknown_system_subtype, _}}, so ClaudeCode.Session.Server dropped the
message and logged a warning for every one (observed 500+ times in a run).
Add ClaudeCode.Message.SystemMessage.Generic — a fallback carrying the raw
subtype string and the full data payload — and have the parser return it for
unrecognized system subtypes. Consumers now receive the event with its data,
and the SDK stays forward-compatible with new CLI system messages without a
release. The subtype is kept as a string (not an atom) since the set of future
subtypes is open-ended.
f126e85 to
cb2db6b
Compare
|
@guess can you please check when you get a chance. |
|
@pshoukry thx for tagging me, i totally missed this- looking now! |
|
Looks good, thanks! Below is claude's review. Also I'm wondering if -- Overall: Good fix — returning a fallback struct instead of dropping unknown system subtypes is the right move, and keeping subtype a string for atom-safety is correct. One change needed before merge: the new Generic struct isn't enrolled in the system-message family, so two existing paths don't recognize it. 🔴 Required: register
Fix is one clause plus the type union: # lib/claude_code/message/system_message.ex
alias __MODULE__.Generic
# add Generic.t() to @type t
def type?(%Generic{type: :system}), do: true🟡 Minor:
|
|
@guess thanks will fix accordingly. I prefer generic since ( we have a subtype returned like thinking_tokens ) but we are not handling it a specific handling. ie: We are handling it generically until we do a specific implementation / handling. Since you aren't feeling strongly about it I will keep it as is for now! |
- Register Generic in SystemMessage.type?/1 and the @type union so Message.message?/1 and Session.Server session-id tracking recognize it (previously Generic events were delivered by the parser but dropped by message?/1 stream filters and ignored for session-id extraction). - Drop dead :unknown_system_subtype from the parser skip set; the parser no longer emits that error. - Correct the Generic.new/1 @doc example to reflect the fields the code sets (type, session_id) so it satisfies @enforce_keys. - Add generic_test.exs covering new/1, generic?/1, and the type?/message? family-enrollment wiring, plus run the doctest.
|
Addressed in 1186162:
On naming: keeping Full suite green (the 2 transient failures I saw locally were MockCLI readiness timeouts under parallelism; both pass in isolation). |
Problem
When a newer Claude Code CLI emits a system-message subtype this SDK version does not model yet (observed:
thinking_tokens), the strict subtype dispatch inClaudeCode.CLI.Parserreturns{:error, {:unknown_system_subtype, subtype}}. As a resultClaudeCode.Session.Serverdrops the message and logs:…once per message (500+ occurrences in a single run), and consumers never see the event.
Fix
Add
ClaudeCode.Message.SystemMessage.Generic— a fallback struct carrying the rawsubtypestring plus the fulldatapayload — and return it from the parser for unrecognized system subtypes:SystemMessage{subtype, data}).subtypeis kept as a string (not an atom), since the set of future subtypes is open-ended (avoids atom-table growth on untrusted-shape input).When a subtype becomes common enough to warrant typed access, it can be promoted to its own
ClaudeCode.Message.SystemMessage.*module and registered in the parser as before.Tests
thinking_tokensexample) parse intoGenericwith the payload preserved.