Repository navigation
Agents: a 409 that is no cancellation fails the turn; an approval denied with 'no' says so - #304
Merged
Merged
Conversation
…pping it A turn took any TerminalError with status 409 for Restate's cancellation, and closed as stopped: but a call fails with 409 for other reasons too. refund_graph.py's payout workflow, run a second time for an order, failed with 409 'the workflow method was already invoked', and the turn said 'Cancelled: the turn was stopped before it finished.'. is_cancellation takes Restate's cancellation error only: 409 'cancelled', which a stopped turn fails with too. The same for a Pydantic AI tool's terminal error, which the model now sees.
…ancelled'
Three tests stood a TerminalError('stopped', 409) in for a stop. A stop,
or Restate cancelling the invocation, fails with 409 'cancelled', which
is_cancellation now tells from other 409s.
…s denied
Any text but an approving one denied the call, with that text as the
reason: a manager's 'no' reached the model as the call's result, and it
answered that 'the tool returned no'. A text that only says no ('no',
'denied', 'rejected'...) denies with Pydantic AI's own message, 'The
tool call was denied.'; any other text is still the reason.
Contributor
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
I reviewed all 3 commits: the narrower check for what counts as a cancellation, the tests updated to match it, and the change to how Pydantic AI handles a bare denial.
is_cancellationreplaces the bare 409 check.answeringin_conversation.pyand Pydantic AI'son_tool_execute_errornow count an error as a stop only when it is aTerminalErrorwith status 409 and the message"cancelled". I checked every place in the repo that produces a cancellation:_turn.cancellation(),server_contextonDoProgressCancelSignalReceived,_preview.py,stored_answerandsessions/_selector.py. All of them raise exactly"cancelled", so a real stop still closes the turn as cancelled. Turns use explicit cancellation, so the stop path never depends on messages the runtime writes.- Other 409s now fail the turn. A workflow invoked a second time, or a child that the runtime itself cancelled or killed, now fails the turn, and Pydantic AI gives the model an
"Error: ..."tool result instead of the falseCANCELLEDanswer. - Tests that stood in for a stop now raise the real error. They raise
TerminalError("cancelled", 409), and the new conversation test pins down how a 409 that is not a cancellation gets stored. - A bare denial uses Pydantic AI's own message.
approval_ofmaps"",no,denied,rejected,declinedandrefused(plus their verb forms) toToolDenied(). Any other text that does not approve is still passed on as the reason.
I ran the in-process tests that touch these paths locally (-k "409 or stopped", approvals_signal), and they pass. The tests that need the Docker harness could not run here.
claude-opus-5-5 | 𝕏
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Found by exercising the agents against a real model (OpenAI) and Restate 1.8.1.
TerminalErrorwith status 409 for Restate's cancellation, and closed as stopped: but a call fails with 409 for other reasons too.refund_graph.py's payout workflow, run a second time for an order (from another conversation), failed with 409 "the workflow method was already invoked", and the turn's answer was "Cancelled: the turn was stopped before it finished.".is_cancellationtakes Restate's cancellation error only, 409 "cancelled", which a stopped turn fails with too (_turn.cancellation, the SDK's cancel signal, a stored cancelled turn). The same for a Pydantic AI tool's terminal error, which the model now sees. Three tests stoodTerminalError("stopped", 409)in for a stop: they raise 409 "cancelled" now.no,denied,rejected...) denies with Pydantic AI's own "The tool call was denied."; any other text is still the reason.Checked live: the second payout of an order now fails the turn with "Error: the workflow method was already invoked"; a
stop, and Restate's cancel (the admin API), of a turn in a tool and of one waiting on a subagent (team.py), still end it cancelled, with the subagents cancelled too. A "no" to a Pydantic AI approval: the model answers that the request was denied.Checks:
ruff format --check,ruff check, pyright and mypy, andpytest tests/*(563 passed) locally; the new test of the 409 fails without the fix.