Skip to content

Anthropic system updates reject otherwise recoverable tool history #51764

Description

@gszep

Description

Anthropic lowering rejects a chronological system update between a local tool call and its result, even when the result is present. It also rejects resumed history where normalizeToolHistory repairs an interrupted call after the update.

The error is Anthropic Messages system updates cannot split a local tool call from its tool result. Could we defer the update until the outstanding results have been emitted?

Related to #51213, but separate from its execution-ownership fix in #51215: this is reproducible entirely in request compilation, without multiple servers or a model call.

Plugins

None needed for the deterministic repro.

OpenCode version

Observed on 2.0.15-linkfix.1. The same rejection remains in v2.0.18 and v2 at 0caae60.

Steps to reproduce

  1. Compile an Anthropic request with the following messages (e.g. claude-opus-5-5):
[
  Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: {} })]),
  Message.system("Environment changed."),
  Message.tool({ id: "call_1", name: "lookup", result: "Done." }),
]
  1. Compilation throws the error above. An error/cancelled tool result has the same outcome.
  2. Replacing the final tool result with Message.user("Continue.") also fails: the existing history normalizer supplies an error result, but leaves the update ahead of it.

Expected: keep the call/results together, then emit the update before the next conversation message. A truly missing terminal result should still be rejected.

Screenshot and/or share link

Non-UI request-compilation bug; synthetic repro above.

Operating System

macOS, Darwin 24.6.0, x64

Terminal

API-driven repro / Bun unit tests.

Activity

github-actions commented on Sep 28, 2026

@github-actions
Contributor

This issue might be a duplicate of or related to existing issues. Please check:

gszep commented on Sep 28, 2026

@gszep
Author

Thanks! #43478 concerns provider placement after a tool result; this repro has the update between the call and its available or repaired result. #51213 concerns competing execution owners (fixed by #51215), and #25774 concerns V1 normalizeMessages splitting co-located tool parts. #51765 addresses the separate deterministic V2 lowering failure; the existing repro comment on #51213 links back here.

rekram1-node commented on Sep 30, 2026

@rekram1-node
Collaborator

This should be impossible to hit in opencode unless:

  • you are doing it with the ai pkg itself
  • you have plugins mutating the message orders

Did you encounter this in opencode???

gszep commented on Sep 30, 2026

@gszep
Author

Yes, we hit it in opencode (2.0.15 with a small unrelated patch), in a real session. Plugins were loaded, but none that reorder messages as far as we can tell.

The trigger was the #51213 situation: the same session was being driven by two opencode server processes at once. One appended an environment system update while the other's tool call was still outstanding, so the misordering came from session writes, not from a plugin. The snippet in the issue uses the ai package directly only to reproduce the rejection without two servers.

If #51215 (one execution owner per session) makes that interleaving unreachable, this is probably moot and I'm happy to close it and #51765.

gszep commented on Sep 30, 2026

@gszep
Author

@rekram1-node allow me a couple of hours to be more confident about the above (written by my orchestrator). but this is my initial diagnosis :)

gszep commented on Sep 30, 2026

@gszep
Author

please don't hesetate to tell me if the orchestrator is being annoying or unhelpful

gszep commented on Sep 30, 2026

@gszep
Author

Follow-up after checking the stored session history rather than inferring.

The session's environment updates alternate between two renderings that differ only in the temp-directory line, which is fixed per process: one server had TMPDIR set, the other (a background service) didn't. They interleave within seconds. At 02:21:00 an assistant step issued a tool call; at 02:21:02 an environment update from the other server was appended and that server started a step, which failed with this error; at 02:21:05 the tool result landed, the first server appended its own environment update and continued normally. The second failure (02:27:35) has the same shape.

So the misordering is in the stored history itself (not the ai package, not a plugin), and it came from two servers driving one session (#51213) on a build without single-owner handling. I found no single-server path. If #51215 makes this unreachable, I'm happy to close this and #51765.

rekram1-node commented on Oct 5, 2026

@rekram1-node
Collaborator

fixed by #52421 and #52426, closing. the two-servers-on-one-session part is tracked in #51215.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions