Replies: 1 comment
|
The rc.7 source confirms the key recovery gap here. That closed turn explains why cold resume does not heal this case. For user recovery I would avoid hand-editing the live compressed artifact. Stop retries and all writers, preserve the original, classify every assistant call id against durable For the durable fix, the loop boundary should close every requested call exactly once before the error turn ends: no durable start → Source-pinned lifecycle trace, recovery decision table, and regression gates: https://sandbaseai.github.io/deepseek-harness-handbook/insufficient-tool-messages.html Disclosure: independent SandBase community handbook, not official DeepSeek documentation. |
Uh oh!
There was an error while loading. Please reload this page.
Summary
After a tool-call turn fails with a scheduler error, the session history can permanently contain an assistant message with
tool_callsthat have no matchingtoolresult messages. From that point on, every LLM request in that session is rejected by the DeepSeek (OpenAI-compatible) API with:This bricks the conversation: the error repeats on every retry/resume until the session is discarded or manually repaired.
Environment
@deepseek-ai/dsh0.1.0-rc.7, desktop GUI (Microsoft Edge WebView client) +dsh webserverdeepseek-official(deepseek-v4-flash), chat-completions wire route (dsh-llm-deepseek)Reproduction evidence (from session logs,
~/.dsh/sessions/*/session.jsonl.zstd)Session A (also seen in a second session):
Turn 2: model emits 2 parallel tool calls (bash + grep). The assistant message is committed with
tool_calls; onetool/callevent is recorded for bash; the second call is never dispatched.The turn then ends in error (never produces any
tool/result):{"type":"turn/end","data":{"turn":2,"reason":{"kind":"error","error":{"message":"Cannot read properties of undefined (reading 'prepare')","code":"UNKNOWN"}}}}From then on, every subsequent turn fails with the 400 above:
{"type":"turn/end","data":{"turn":3,"reason":{"kind":"error","error":{"message":"An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'. ..."}}}}Root cause
In
dsh-agent-loop's tool-call scheduler (runGroup), when the scheduler throws (here:ctx.tools[TOOL_RUNTIME_SCHEDULER]wasundefinedwhen calling.prepare(...),TypeError: Cannot read properties of undefined (reading 'prepare')):The
tool/callevent was already appended (beforeprepare), but the failure path intentionally does not fabricate results — the code comment says: "A terminal scheduler failure preserves already-recorded tool/call events without fabricating results." The abort path does fabricate results (appendSkippedToolCall), but the error path does not.Net effect: the persisted conversation history contains an assistant message with
tool_callsand no answeringtoolmessages.dsh-llm-deepseek/serializeMessagesserializes that history verbatim to the wire, so the provider rejects the request. This also permanently poisons the session for compaction/resume.Suggested fixes
tool/result(mirroringappendSkippedToolCall), so the history never retains danglingtool_calls.serializeMessages, append synthetic{role:"tool", tool_call_id}messages for any assistanttool_callsid that never receives atool-result, so a malformed stored history can never produce this provider 400.Workaround (users)
Start a new conversation, or manually append synthetic
tool/resultevents (withisError: true) to the session JSONL for each unansweredtool_call_id.All reactions