Repository navigation
Reject duplicate durable toolset ids and fail deferred tools with UnsupportedToolDeferralError - #74902
Merged
Conversation
… durable=True AgentOperator(durable=True) checked only that each FunctionToolset, MCPToolset and DynamicToolset had an id, so two toolsets with the same id passed construction and failed at run time with pydantic-ai's "Two toolsets have the same id" UserError. It now refuses them with ValueError when the Dag is parsed. A tool needing approval under durable=True failed with pydantic-ai's "DeferredToolRequests is not among output types" UserError, after the model had been called, instead of the documented UnsupportedToolDeferralError. Toolsets that declare approval (.approval_required(), requires_approval=True, ExternalToolset) are now refused at construction, and a tool that raises ApprovalRequired or CallDeferred itself ends the run and fails the task with UnsupportedToolDeferralError.
Drop the parse-time refusal of approval and external tools under durable=True. It rejected configurations that ran to success: a HandleDeferredToolCalls judge that settles calls inline, conditional approval that is not triggered, CodeMode, and marked tools the model never calls. A deferral that does end the run still fails the task with UnsupportedToolDeferralError. The duplicate-id check now looks only at the toolsets the agent runs, so agent_params["toolsets"] no longer counts when toolsets= replaces it. A deferral that fails the task now pushes the attempt's usage and run_id XComs, as other failed attempts do.
durable=TrueUnsupportedToolDeferralError
phanikumv
approved these changes
Oct 17, 2026
1 task done
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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 while testing common.ai 1.0.0rc1 (#74741).
AgentOperator(durable=True)documents two behaviours it did not have.Duplicate toolset ids. The 1.0.0 changelog and the durable execution page say every
FunctionToolset,MCPToolsetandDynamicToolsetneeds a uniqueid=, checked when the operator is created. Construction only checked that an id was present. Two toolsets sharingid="orders"loaded fine and then failed at run time with pydantic-ai'sUserError: Two toolsets have the same id 'orders'. The operator now raisesValueErrorwhen it is created. It applies the same rule pydantic-ai's durable backend does (durable_exec/_base.py): only distinct instances of those three kinds collide, and one instance passed in two places counts once. The check covers the toolsets the agent actually runs, soagent_params["toolsets"]doesn't count whentoolsets=replaces it.A tool that defers under
durable=True. The durable page says such a call fails the task withUnsupportedToolDeferralError. It actually failed with pydantic-ai'sUserError: A deferred tool call was present, but DeferredToolRequests is not among output types. The operator addedDeferredToolRequeststo the output types only when approval was supported, so its own check was never reached. Durable runs now include it, so a deferral that ends the run fails the task withUnsupportedToolDeferralErrorand pushes the attempt'susageandrun_idXComs, as other failed attempts do.Why approval tools aren't refused when the Dag is parsed. A declared approval tool often never pauses a durable run. A
HandleDeferredToolCallsjudge can settle every call inline, conditional approval may not trigger, CodeMode reports the call back to the model, or the model may never call the tool. All of these ran to success onmain, and a parse-time check would have broken them. The new tests cover the judge case underdurable=True.Without the source change, the new tests fail with
DID NOT RAISE ValueError,KeyError: 'usage'and theUserErrorabove.