Pydantic AI Dangling Tool Call Index Confusion Leads to Unauthorized Tool Execution
First seen Jul 30, 2026 · Updated Jul 30, 2026 · CVSS 6.5
Pydantic AI's UI adapters (AG-UI and Vercel AI) had a message-sanitization bug where a stale index calculation allowed a crafted client message history to reintroduce an unresolved, client-controlled tool call as if it were the model's own output. This let a remote client trigger execution of registered server tools with attacker-chosen arguments, bypassing model-request guardrail hooks, though approval-gated tools were not affected. The vendor has patched this in 1.107.1 and 2.5.0.
Technical Analysis
The sanitize_messages defense strips dangling (unresolved) tool calls from untrusted client-submitted message history before the agent processes it, intending to ensure only model-emitted tool calls are executed. The flaw is a logic/index bug: the 'tail' position used to identify and strip the dangling call was computed before sanitization completed, so when a trailing client message (e.g., a system message under manage_system_prompt='server') was sanitized away entirely, the message array shifted and a preceding assistant message containing an unresolved tool call became the new tail — which was then dispatched without the intended inspection. This crosses a trust boundary because message history is client-controlled input in these UI-facing adapters, and the bug effectively lets a remote client forge tool-call dispatch, skipping the model turn entirely. The impact is bounded by which tools are registered without requires_approval=True, but any application that relies on before_model_request/after_model_request hooks as a security control is directly undermined since the forged call never traverses that hook.
Affected Systems
Pydantic AI (Agent.to_ag_ui, AGUIAdapter, VercelAIAdapter); protocols: AG-UI, Vercel AI SDK protocol
Detection Signatures
- Server logs showing tool invocation events with no corresponding preceding model-generated tool_call in the canonical (server-side) conversation trace
- Client-submitted message payloads containing trailing system/user messages that would sanitize to empty combined with an assistant message holding an unresolved tool_call
- Tool executions occurring without a matching before_model_request/after_model_request hook invocation in trace logs
- Unexpected tool argument values inconsistent with model-generated completions in stored transcripts
- Applications using manage_system_prompt='server' with UI adapters prior to patched versions
Remediation Steps
- 1
Upgrade Pydantic AI
Update to version 1.107.1 or 2.5.0 (or later) where the index computation for stripping dangling tool calls was fixed to occur after sanitization completes.
- 2
Enforce approval gating on sensitive tools
Set requires_approval=True on any registered server tool capable of meaningful side effects, since this path does not auto-execute approval-gated tools.
- 3
Do not rely solely on model-request hooks for authorization
Treat before_model_request/after_model_request as observability/logging aids rather than the sole security boundary; enforce tool-level authorization checks independent of model turn flow.
- 4
Validate and reconcile message history server-side
Cross-check that any dispatched tool call corresponds to an actual, current model completion rather than trusting client-supplied history state after sanitization.
- 5
Audit UI adapter usage
Review all AG-UI and Vercel AI adapter integrations for use of manage_system_prompt='server' and confirm sanitize_messages behavior post-patch via regression testing with crafted trailing-message payloads.
CVE / Advisory IDs
Industries Most Exposed
Respond to this threat
Pro subscribers get a full AI-generated incident-response playbook for this threat — detection, containment, eradication, and recovery steps — plus an unlimited AI Threat Advisor for questions about your environment.