JuliusBrussee/caveman · error · ValueError
Pydantic AI middleware requires a native conversation_id
Error message
Pydantic AI middleware requires a native conversation_id
What it means
scope_from_run builds a Caveman Scope from a Pydantic AI RunContext and requires the framework's native conversation_id to identify the conversation. Pydantic AI omitted or supplied an empty conversation_id, so no stable scope key can be derived and a ValueError is raised.
Solutions
- Ensure the Pydantic AI run has a conversation_id before invoking the middleware (enable the framework's conversation/session id generation)
- Construct test RunContext objects with a non-empty conversation_id string
- Pass a custom scope resolver to the adapter instead of scope_from_run if your app supplies its own conversation identity
Example fix
// before
scope = scope_from_run(ctx, namespace="app") # ctx.conversation_id is None
// after
if ctx.conversation_id:
scope = scope_from_run(ctx, namespace="app")
else:
ctx.conversation_id = str(uuid4())
scope = scope_from_run(ctx, namespace="app") Defensive patterns
Strategy: validation
Validate before calling
if not isinstance(getattr(ctx, "conversation_id", None), str) or not ctx.conversation_id:
raise ValueError("RunContext needs a non-empty conversation_id before middleware scoping") Type guard
def has_conversation_id(ctx) -> bool:
return isinstance(getattr(ctx, "conversation_id", None), str) and bool(ctx.conversation_id) Try / catch
try:
scope = scope_from_run(ctx, namespace="app")
except ValueError as e:
if "conversation_id" in str(e):
ctx.conversation_id = str(uuid4())
scope = scope_from_run(ctx, namespace="app")
else:
raise Prevention
- Enable Pydantic AI conversation/session ids in your agent setup
- In tests, always set a non-empty conversation_id on RunContext fixtures
- After upgrading pydantic-ai, verify conversation_id is populated on your run paths
- Or supply your own resolver returning a Scope built from app-side identity
When it happens
Trigger: Calling scope_from_run(ctx, namespace=...) with ctx.conversation_id set to None or "" — typically when the RunContext was constructed manually or the framework version does not populate conversation_id for this run path.
Common situations: Unit tests building RunContext fixtures without conversation_id; upgrading pydantic-ai to a version where conversation_id is populated later in the run lifecycle; custom agent loops that never set a conversation id.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Pydantic AI scope resolver must return a Caveman Scope
- Agno middleware requires a nonempty native session_id
- Agno scope resolver must return a Caveman Scope
- AutoGen requires a stable Caveman Scope for each agent or…
- Expected an existing native Pydantic AI Model
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/7ba115d4fe88134d.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/pydantic_ai.py:38
from pydantic_ai.models import Model, ModelRequestContext, ModelRequestParameters
from pydantic_ai.models.wrapper import WrapperModel
from pydantic_ai.toolsets import FunctionToolset
from pydantic_core import PydanticSerializationError
except ModuleNotFoundError as error:
raise ImportError("Install caveman-middleware[pydantic-ai] to use the Pydantic AI adapter") from error
from caveman_cloud.middleware import Adapter, Candidate, MiddlewareError, MiddlewareRuntime, Scope
from caveman_cloud.middleware.runtime import RECOVERY_DESCRIPTION, RECOVERY_SCHEMA
from ._native import Attempt, manifest, owner
from ._versions import supports_framework
ADAPTER = Adapter("pydantic-ai", "0.1.0", "2.42.0", "pydantic-ai-message-v1")
def scope_from_run(ctx: RunContext, *, namespace: str) -> Scope:
"""Use native conversation identity plus application-owned branch metadata."""
if not isinstance(ctx.conversation_id, str) or not ctx.conversation_id:
raise ValueError("Pydantic AI middleware requires a native conversation_id")
metadata = ctx.metadata or {}
return Scope(namespace, ctx.conversation_id, metadata.get("caveman_branch_id", "main"),
metadata.get("caveman_cache_epoch", "0"))
def _scope(source, ctx=None):
scope = source if isinstance(source, Scope) else source(ctx)
if not isinstance(scope, Scope):
raise TypeError("Pydantic AI scope resolver must return a Caveman Scope")
return scope
def _runtime(runtime):
return runtime.as_async() if isinstance(runtime, MiddlewareRuntime) else runtime
def _check_version(runtime):
return supports_framework(runtime, ("pydantic-ai-slim", "2.42", "3"))View on GitHub (pinned to 3ee70a1026)