I think the Claude auth plugin may be part of the failure path here, but I can’t prove it’s the original cause.
What I’m seeing: after using /undo and then /compact in OpenCode, some sessions start failing on Sonnet with:
messages.2: tool_use ids were found without tool_result blocks immediately after ... Each tool_use block must have a corresponding tool_result block in the next message.
If I switch the same session from Sonnet to GPT, it works.
That makes me think one of two things is happening:
- OpenCode is producing slightly malformed history after undo/compact
- this plugin is not repairing / normalizing that history enough for Anthropic’s stricter validation
The reason I’m opening this here is that opencode-claude-auth does touch the message payload and already has repairToolPairs() in dist/transforms.js.
From what I can tell, that function checks whether matching tool_use / tool_result ids both exist somewhere in the message list, but it does not enforce the Anthropic rule that the matching tool_result has to be in the very next message.
So if undo/compact leaves history in a state where:
- message N has a
tool_use
- the matching
tool_result still exists, but not immediately after
then GPT may still tolerate it, but Sonnet rejects it, and the plugin lets it through because the pair technically still exists.
I inspected one broken session locally and found exactly that shape: the assistant message had the tool_use, and the next message was another assistant message, not the matching tool result wrapper.
So to be clear: I’m not saying this plugin necessarily creates the bad history. I suspect OpenCode core may be the original source, especially with /undo + /compact. But I do think this plugin may be related because it’s the Claude-specific path, and its repair logic seems too shallow for Anthropic’s adjacency requirement.
If helpful, I can put together a cleaner repro next time it happens. Right now the strongest evidence is:
- broken session on Sonnet
- same session works on GPT
- happened after /undo + /compact
- plugin has tool-pair repair logic, but it appears to check existence rather than adjacency
Thanks — posting here because this feels close enough to the Claude transform layer that it seemed worth flagging.
I think the Claude auth plugin may be part of the failure path here, but I can’t prove it’s the original cause.
What I’m seeing: after using /undo and then /compact in OpenCode, some sessions start failing on Sonnet with:
messages.2:
tool_useids were found withouttool_resultblocks immediately after ... Eachtool_useblock must have a correspondingtool_resultblock in the next message.If I switch the same session from Sonnet to GPT, it works.
That makes me think one of two things is happening:
The reason I’m opening this here is that opencode-claude-auth does touch the message payload and already has
repairToolPairs()indist/transforms.js.From what I can tell, that function checks whether matching
tool_use/tool_resultids both exist somewhere in the message list, but it does not enforce the Anthropic rule that the matchingtool_resulthas to be in the very next message.So if undo/compact leaves history in a state where:
tool_usetool_resultstill exists, but not immediately afterthen GPT may still tolerate it, but Sonnet rejects it, and the plugin lets it through because the pair technically still exists.
I inspected one broken session locally and found exactly that shape: the assistant message had the
tool_use, and the next message was another assistant message, not the matching tool result wrapper.So to be clear: I’m not saying this plugin necessarily creates the bad history. I suspect OpenCode core may be the original source, especially with /undo + /compact. But I do think this plugin may be related because it’s the Claude-specific path, and its repair logic seems too shallow for Anthropic’s adjacency requirement.
If helpful, I can put together a cleaner repro next time it happens. Right now the strongest evidence is:
Thanks — posting here because this feels close enough to the Claude transform layer that it seemed worth flagging.