Pydantic AI 2.65.0, published on November 3, 2026, changes what some AI assistants do when a model provider declines a request: they can now pass it to an already-configured backup. Developers and automation owners using its ModelFallback capability need to review this change in refusal routing before upgrading.
The practical choice is whether to retain a refusal or try an approved backup. This change applies to non-streamed responses marked as content-filter refusals. It does not select a new provider for you, and the backup still applies its own provider’s safety policy. Pydantic’s version-pinned guide describes those limits.
Find the configuration that changes
Check the class name, not just whether your application has “fallback” somewhere in its settings:
ModelFallbacknow tries the next configured model after a marked refusal by default, whether the response is empty or contains text. Its default isfallback_on=(ModelAPIError, ContentFilterError), in both Python and agent specifications. See the released capability.FallbackModel, the older model wrapper, retains its API-error-only default. However, explicitly includingContentFilterErrornow enables refusal fallback; that setting previously did not match these responses. The compatibility notice distinguishes the two interfaces.An agent without either fallback mechanism does not acquire a backup through this change. The ordinary behavior remains an error for an empty refusal, while refusal text can become output. See Pydantic’s refusal-error guide.
The trigger is structured metadata: finish_reason='content_filter'. It is not a search for refusal-like wording in the answer. The shared implementation also distinguishes explicitly naming ContentFilterError from naming a broader exception class such as Exception. Review custom response handlers separately: they can reject responses under their own rules.
Keep the previous routing
For ModelFallback, set fallback_on=(ModelAPIError,) to preserve its previous default: move to a backup on a provider API error, but not on a marked refusal. The maintainers’ migration instructions give this opt-out.
Adapt this configuration fragment inside your existing agent setup. approved_backup_model stands for your existing model object or model name; retain any additional candidates, other options and unrelated capabilities.
from pydantic_ai.capabilities import ModelFallback
from pydantic_ai.exceptions import ModelAPIError
ModelFallback(
approved_backup_model,
fallback_on=(ModelAPIError,),
)For a YAML agent specification, set fallback_on: [ModelAPIError] inside the existing ModelFallback mapping, preserving its models and other settings. The specification implementation supports the same choice. In JSON, write the setting as "fallback_on": ["ModelAPIError"].
For legacy FallbackModel, leave the default alone if it already expresses your policy. If you explicitly listed ContentFilterError but want the earlier routing, remove that entry while preserving the other intended conditions. Do not leave an empty fallback_on: it is invalid. If refusal was the only condition, decide whether to remove the fallback wrapper or deliberately use its API-error-only default. See the wrapper and configuration validation.
The 2.65.0 package metadata confirms availability on PyPI and a Python 3.11 minimum.
Decide whether refusal text is output or an error
Keeping the previous route is not the same as making every refusal an error. With the API-error-only setting, an empty refusal raises ContentFilterError, but a refusal containing text can return that text as the run’s output. Pydantic’s version-pinned regression cases specify both outcomes and check that the backup is not called.
If your application must treat every marked refusal as an error without forwarding it, combine the routing opt-out with RaiseContentFilterError() in the existing capability list. Import it from pydantic_ai.capabilities. The capability’s documentation explains how it extends error handling to partial and refusal text.
Adding that error capability alone is not a routing opt-out: ModelFallback evaluates the response first. Set the refusal-routing condition explicitly as well.
If continuing to an approved backup is the intended policy, the new ModelFallback default provides it. An explicit fallback_on=(ModelAPIError, ContentFilterError) makes that choice visible; FallbackModel needs this explicit inclusion. A backup does not guarantee an answer. When every candidate fails or is rejected, the fallback machinery raises FallbackExceptionGroup. Handle that outcome too.
A handoff concerns the current request and its existing message history, not a restart of the whole workflow. Review backup eligibility for that context. See the request-level behavior.
Check the real application path before rollout
Streaming is a separate case. This refusal fallback does not apply after a response has been streamed, although eligible errors before the first stream event can still trigger fallback. Event handlers, listeners and UI adapters can enable streaming even when the application calls run() or run_sync(). Check the documented streaming paths, not only the method name.
Suggested acceptance checks: use deterministic model stand-ins and harmless substitutes for business tools in an isolated setup. Pydantic’s upstream refusal tests provide examples of empty and text-bearing responses; the checks below are recommendations, not reported results:
Send an empty refusal and a text-bearing refusal through the actual application entry point. Assert which models were called and whether the application received output or an error.
Exercise an ordinary API failure and a normal successful response. Confirm the refusal-policy edit has preserved the intended behavior for both.
If refusal fallback is enabled, make every candidate decline. Check the final error and confirm no consequential tool action occurs merely because a backup was attempted.
Repeat through the deployed streaming or event-consumer path. If
sticky=Trueis already enabled, include a second turn using the continued message history: the capability can keep later requests on the answering backup, although explicit model selection can take precedence.
For operations, inspect the answering model and failed_attempts, alongside aggregate usage. Rejected responses count toward run usage, but the failed-attempt record omits provider_details. A second attempt can add waiting time and charges; actual billing depends on the provider, not simply the usage record. The fallback guide explains the accounting and model-switch tradeoffs.
Keep tool permissions separate from the decision to ask another model. A backup answer does not itself authorize a business action. For teams also using Claude’s agent framework, the related guide to blocking actions when custom checks fail addresses that separate application-control problem.
