Skip to content

[runtime] Do not retry or persist interrupted tool calls on cancellation - #1111

Open
Ashfaqbs wants to merge 1 commit into
apache:mainfrom
Ashfaqbs:fix/tool-call-interrupted-persistence
Open

[runtime] Do not retry or persist interrupted tool calls on cancellation#1111
Ashfaqbs wants to merge 1 commit into
apache:mainfrom
Ashfaqbs:fix/tool-call-interrupted-persistence

Conversation

@Ashfaqbs

@Ashfaqbs Ashfaqbs commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Linked issue: #1088

Purpose of change

ToolCallAction.executeSequentially/executeParallel caught InterruptedException the same as any ordinary tool failure, recorded it as a tool error, and let the loop continue — processToolRequest still sent a ToolResponseEvent afterward, driving another chat call and letting ActionExecutionOperator persist the action as completed. On recovery, isCompleted() then skipped re-execution and replayed the stale output instead of re-running the interrupted call — the same failure mode #1071 closed for the chat-call path, reached here through the tool-call path #1071 didn't cover (out of that PR's scope, filed separately per the discussion on #1071).

executeParallel's batch path had the same gap one layer down too: RunnerContextImpl.durableExecuteAllAsync's per-callable catch (Exception e) folded an interruption into Outcome.failure and kept scheduling the rest of the batch, even though durableExecute itself (fixed in #1071) already rethrows InterruptedException correctly — this outer catch was just re-swallowing it before it could propagate.

Behavioral Semantics

Fixed both the same way #1071 fixed the chat-call path: an explicit catch (InterruptedException e) ahead of the generic failure handling restores the interrupt flag and rethrows immediately, in three places:

  • RunnerContextImpl.durableExecuteAllAsync — stops scheduling the remaining callables in the batch and propagates, instead of recording a failed Outcome and continuing.
  • ToolCallAction.executeParallel — propagates instead of recording every execution in the batch as a tool error.
  • ToolCallAction.executeSequentially — propagates instead of recording the current execution as a tool error and moving on to the next one in the loop.

processToolRequest now declares throws InterruptedException and lets it propagate, so sendEvent for the ToolResponseEvent is skipped entirely on a cancellation — no further chat call gets driven off a cancelled tool call, and the action is never persisted as completed on the back of it. Ordinary tool failures (missing tool, execution exceptions, infrastructure errors) are unaffected — they still go through the existing recording/reporting paths untouched.

Tests

  • Added 3 cases to ToolCallActionTest: an interrupted single (sequential) call propagates and sends no ToolResponseEvent; a second sequential tool call does not run once the first is interrupted; an interrupted parallel batch propagates and sends no ToolResponseEvent. All three assert the interrupt status is restored on the thread.
  • Added a case to RunnerContextImplDurableExecuteTest: in a 3-callable batch where the 2nd is interrupted, the 1st (already-succeeded) callable's result is correctly persisted, the 2nd's interruption is not recorded as a failed outcome, and the 3rd never runs.
  • ToolCallActionTest: 19/19 passing (16 pre-existing + 3 new). RunnerContextImplDurableExecuteTest: 14/14 passing (13 pre-existing + 1 new).
  • Full plan module suite: 313/316 passing; the 3 failures are the pre-existing BashToolTest cases (require a working WSL bash on this Windows environment, fail identically on main before this diff — confirmed via git stash).
  • Full runtime module suite: 847/854 passing; all 14 failures are pre-existing, environment-specific (Fluss coordinator server not starting, Windows path/file-locking issues in unrelated skill-loading and Python-environment tests) — none in ToolCallActionTest or RunnerContextImplDurableExecuteTest.
  • spotless:check clean on both modules.

API

No public API changes. ToolCallAction.processToolRequest/executeSequentially/executeParallel now declare throws InterruptedException, but these are internal, reflectively-invoked action handlers (JavaFunction), not part of the public surface.

Documentation

  • doc-needed
  • doc-not-needed
  • doc-included

Was this patch authored or co-authored using generative AI tooling?

  • Yes
  • No

Generated-by: Claude Code 2.1.226 (Claude Sonnet 5)

ToolCallAction.executeSequentially/executeParallel caught
InterruptedException the same as any ordinary tool failure, recorded
it as a tool error, and let the loop continue: processToolRequest
still sent a ToolResponseEvent afterward, driving another chat call
and letting ActionExecutionOperator persist the action as completed.
On recovery, isCompleted() then skipped re-execution and replayed the
stale output instead of re-running the interrupted call - the same
failure mode apache#1071 closed for the chat-call path, reached here through
the tool-call path apache#1071 didn't cover.

executeParallel's batch path had the same gap one layer down too:
RunnerContextImpl.durableExecuteAllAsync's per-callable catch (Exception e)
folded an interruption into Outcome.failure and kept scheduling the
rest of the batch, even though durableExecute itself (fixed in apache#1071)
already rethrows InterruptedException correctly - this outer catch was
just re-swallowing it before it could propagate.

Fixed both the same way apache#1071 fixed the chat-call path: restore the
interrupt flag and rethrow immediately, so the loop stops, no
ToolResponseEvent goes out, and the action is never persisted as
completed off the back of a cancelled call.

Fixes apache#1088

Generated-by: Claude Code 2.1.226 (Claude Sonnet 5)
@github-actions github-actions Bot added doc-not-needed Your PR changes do not impact docs fixVersion/0.4.0 priority/major Default priority of the PR or issue. labels Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

doc-not-needed Your PR changes do not impact docs fixVersion/0.4.0 priority/major Default priority of the PR or issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant