Skip to content

fix(history): don't parse tool_result as ItemStartedEvent in deferred dequeue#1

Open
Antisophy wants to merge 1 commit into
CyberShadow:masterfrom
Antisophy:fix/history-tool-result-after-dequeue
Open

fix(history): don't parse tool_result as ItemStartedEvent in deferred dequeue#1
Antisophy wants to merge 1 commit into
CyberShadow:masterfrom
Antisophy:fix/history-tool-result-after-dequeue

Conversation

@Antisophy

Copy link
Copy Markdown

Problem

Opening a task whose history has a steering message immediately followed by a tool_result crashes the server with an uncaught object.Exception (exit 1). ensureHistoryLoaded throws on every click, so the task's history never loads.

Abbreviated stack trace:

object.Exception@ae/utils/serialization/serialization.d(1007): Unknown field tool_result
  ... jsonParse!(cydo.agent.protocol.ItemStartedEvent) ...
  source/cydo/app.d  (ensureHistoryLoaded, deferred-dequeue branch)

Root cause

In the deferred-dequeue branch, after a queue dequeue the code treats the next type:"user" line as the steering echo and parses it strictly as ItemStartedEvent. But when the turn was interrupted mid-tool-use, that line is a tool_result, which translateHistoryLine turns into an item/result event whose tool_result field ItemStartedEvent doesn't declare. ItemStartedEvent is parsed strictly, so the unknown field throws and the exception is uncaught.

Fix

Peek at the translated event's type with a @JSONPartial probe and only run the ItemStartedEvent parse/inject path when it's item/started. Otherwise emit the translated events unchanged and stay deferred for the real echo. ItemStartedEvent is left strict so round-tripping an item/result through it can't silently drop tool_result.

Repro

A Claude history file with these three lines; open the task in the UI:

{"type":"queue-operation","operation":"enqueue","timestamp":"2026-06-11T06:00:00Z","sessionId":"S","content":"are you under control?"}
{"type":"queue-operation","operation":"dequeue","timestamp":"2026-06-11T06:00:01Z","sessionId":"S"}
{"parentUuid":"p","isSidechain":false,"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_1","type":"tool_result","content":"ok"}]}}

… dequeue

After a queue dequeue, the deferred branch assumed the next type:"user" line
is the steering echo and parsed it strictly as ItemStartedEvent. When a turn
was interrupted mid-tool-use, that line is a tool_result, which translates to
an item/result event whose tool_result field ItemStartedEvent doesn't declare,
throwing an uncaught Exception that crashes history loading and prevents the
task from ever opening.

Peek at the translated event type with a @JSONPartial probe and only run the
ItemStartedEvent parse/inject path for item/started; otherwise pass the events
through unchanged and stay deferred for the real echo. ItemStartedEvent stays
strict so item/result round-trips don't drop tool_result.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant