Messages 协议拒绝遗留的 reasoning 内容,导致旧会话永久打不开 #6864
bozhang1214
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Environment
0.1.6-alpha.1(profile:web)deepseek-official, wire protocolmessages— the default sinceb0641b83fcSymptom
A pre-existing session fails on every turn with:
Retrying, switching models, and restarting the process all fail. Setting
protocol: chat-completionsmakes the same session work again, which points at the protocolrather than at the data.
Root cause: the two protocols disagree on identical durable history
packages/llm/llm-deepseek/src/protocols/messages/serialize.ts:11-13, 57-59accepts onlytextandimagein a user/tool-result position and throws for anything else.Scanning
~/.dsh/sessions/**/session.v3.jsonl.zstdfound 7user/messageevents in onesession whose
data.contentcarries areasoningblock directly. All 7 come from the sameproducer — the background-subagent settlement notice, which inlined the child's closing
content blocks verbatim into the parent's user message (timestamps 2026-09-03 and 09-09).
That producer was already fixed:
29debb8b24 fix(subagent): keep settlement notices text-only(2026-09-14) —packages/subagent/subagent/src/continuation-messages.ts:143now keeps text blocks only. Buthistory persisted before that date cannot be rewritten, and Messages fails closed on it.
Running the same legacy user message through both serializers:
chat-completions'
flattenText()keeps only text blocks and silently drops the rest(
serialize.ts:95-100); itsassertTextOnly()rejects only images. Messages throws for anynon-
text/non-imageblock.So this is not data corruption — it is a contract mismatch between the two protocols.
Because
0.1.6-alpha.1mademessagesthe default, that mismatch silently made a class ofexisting sessions permanently unusable.
Why simply dropping every non-text block is wrong
Scanning all local sessions for non-text blocks in user/tool-result positions gives
reasoning(20),file(5),image(47).imageis already supported. Afileblock isreal model-visible input — discarding it would silently change what the model sees — so it
must keep failing closed. Only
reasoningcarries no model-visible meaning in that position,and chat-completions already drops it.
Suggested fix
Treat
reasoningin a user/tool-result position the way chat-completions already does: dropit and report the degradation through the existing
onReplayDegradechannel, keeping everyother unrepresentable block fail-closed.
Patch (1 commit, 2 files, +30/−1 —
serialize.ts+ its spec, on my fork; no PR per CONTRIBUTING):bozhang1214/deepseek-harness@bff8e9a...225ffa1
Alternatives the team may prefer:
Session.deriveMessages()/ the surface fold) and keep theprotocol serializers strict. Caveat:
SessionMessageProjectionis designed forplugin-owned event types, so intercepting the core
user/messagewould replace the built-inderivation.
rewrites user history.
All three need the same underlying decision: legacy history must be allowed to degrade,
because the producer fix can only apply going forward.
Verification
reasoningin auser/tool-result position is dropped with a degradation diagnostic;
text/imageunchanged; every other type still fails closed.
assertion that previously required a throw into a
tool-callfail-closed assertion(
packages/llm/llm-deepseek/tests/messages/serialize.spec.ts:299on upstream master).llm-deepseekpackage 577/577 pass;oxlintclean.
messagesreturns[{"type":"text","text":"Its closing message:"}]— identical to chat-completions — plus thedegradation diagnostic.
affected session end-to-end against the provider yet (that needs a process restart).
Impact
Any session persisted before 2026-09-14 that used a background subagent becomes permanently
unusable once the
messagesprotocol becomes the default — and it fails on every single turn.The error names only "content reasoning" and never hints that the data is legacy or that
chat-completions still handles it, so users are unlikely to find the cause.
Related, but not duplicate
Separate from the
dsh_session_logfirst-upload issue (HTTP 413): that one is about requestbody size, this one about representable content.
All reactions