Description
If writing a local tool's result to the DB fails (I hit SQLiteError: database or disk is full), the step still ends normally, and the next request goes out with a tool_use that has no tool_result. Anthropic rejects it:
messages.16: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_01Rd…
The call stays running in the DB until the session is interrupted.
Cause, in packages/core/src/session/runner/publish-llm-event.ts: toolExecution and failTool set tool.settled = true before bus.publish. When the publish fails, the in-memory flag stays set, so failUnsettledTools skips the call in step.ts and never records a Tool.Failed. toolExecution has the same problem with its "no content" defect, which also runs after the flag is set.
How I hit it: a parent session started three subagent calls. One child failed with SQLITE_FULL partway through its run. When the other two finished, the parent sent its next request and got the 400 above.
Reproduced on v2 at 80caae8 with two runner tests, one per path. In each, the follow-up request contains a tool-call with no result:
toolExecution: the Tool.Success projection dies for one call.
failTool: a tool returns Tool.Error, and the first Tool.Failed projection dies. The step's failUnsettledTools should retry it, but skips it.
I have a small fix with both tests: clear the flag again if the publish fails, and check for content before setting it. The check-and-mark stays atomic, per the publisher's concurrency note. Happy to open a PR.
Plugins
superpowers, openchamber agent-tool, a couple of local plugins (none of them affect this path; the repro is a core unit test)
OpenCode version
2.0.19 (still reproduces on v2 @ 80caae8)
Steps to reproduce
- Run a session where a local tool call's
session.tool.success write fails. Easiest way is a nearly full disk (SQLITE_FULL), or in tests, bus.project(SessionEvent.Tool.Success, () => Effect.die(...)).
- Have the provider end the step with
tool-calls.
- The continuation request contains the
tool_use with no tool_result, and Anthropic returns a 400.
Test case:
scenario("never replays a local tool call whose result failed to persist", function* (s) {
yield* s.bus.project(SessionEvent.Tool.Success, (event) =>
event.data.id === "call-store-orphan" ? Effect.die("SQLiteError: database or disk is full") : Effect.void,
)
yield* s.llm.push(
TestLLM.toolCalls(LLMEvent.toolCall({ id: "call-store-orphan", name: "storefail", input: {} })),
TestLLM.stop(),
)
yield* s.runPrompt("Storage fails after the tool ran").pipe(Effect.exit)
// fails on v2: requests[1] has tool-call "call-store-orphan" with no tool-result
})
Screenshot and/or share link
N/A
Operating System
macOS (Darwin 25.3.0 arm64)
Terminal
OpenChamber (desktop)
Description
If writing a local tool's result to the DB fails (I hit
SQLiteError: database or disk is full), the step still ends normally, and the next request goes out with atool_usethat has notool_result. Anthropic rejects it:The call stays
runningin the DB until the session is interrupted.Cause, in
packages/core/src/session/runner/publish-llm-event.ts:toolExecutionandfailToolsettool.settled = truebeforebus.publish. When the publish fails, the in-memory flag stays set, sofailUnsettledToolsskips the call instep.tsand never records aTool.Failed.toolExecutionhas the same problem with its "no content" defect, which also runs after the flag is set.How I hit it: a parent session started three
subagentcalls. One child failed withSQLITE_FULLpartway through its run. When the other two finished, the parent sent its next request and got the 400 above.Reproduced on
v2at 80caae8 with two runner tests, one per path. In each, the follow-up request contains a tool-call with no result:toolExecution: theTool.Successprojection dies for one call.failTool: a tool returnsTool.Error, and the firstTool.Failedprojection dies. The step'sfailUnsettledToolsshould retry it, but skips it.I have a small fix with both tests: clear the flag again if the publish fails, and check for content before setting it. The check-and-mark stays atomic, per the publisher's concurrency note. Happy to open a PR.
Plugins
superpowers, openchamber agent-tool, a couple of local plugins (none of them affect this path; the repro is a core unit test)
OpenCode version
2.0.19 (still reproduces on v2 @ 80caae8)
Steps to reproduce
session.tool.successwrite fails. Easiest way is a nearly full disk (SQLITE_FULL), or in tests,bus.project(SessionEvent.Tool.Success, () => Effect.die(...)).tool-calls.tool_usewith notool_result, and Anthropic returns a 400.Test case:
Screenshot and/or share link
N/A
Operating System
macOS (Darwin 25.3.0 arm64)
Terminal
OpenChamber (desktop)