langchain-ai/deepagents · error · ValueError
models can only be provided when agent is a factory
Error message
models can only be provided when agent is a factory
What it means
The model half of a `ModelSpec` is required; `__post_init__` raises `ValueError` when `model` is empty. This guarantees every spec resolves to a concrete model so downstream `create_model` calls never receive a blank model id.
Source
Thrown at libs/acp/deepagents_acp/server.py:260
models: Optional list of available models with 'value', 'name', and optionally
'description'
load_sessions: Advertise and implement durable `session/load`. The agent graph
must use a checkpointer that survives server restarts.
"""
super().__init__()
self._cwd = ""
self._agent_factory = agent
self._agent: CompiledStateGraph | None = None
self._agent_session_id: str | None = None
self._load_sessions = load_sessions
if isinstance(agent, CompiledStateGraph):
if modes is not None:
msg = "modes can only be provided when agent is a factory"
raise ValueError(msg)
if models is not None:
msg = "models can only be provided when agent is a factory"
raise ValueError(msg)
self._modes: SessionModeState | None = None
self._models: list[dict[str, str]] | None = None
else:
self._modes = modes
self._models = models
self._session_modes: dict[str, str] = {}
self._session_mode_states: dict[str, SessionModeState] = {}
self._session_models: dict[str, str] = {} # Track current model per session
self._cancelled = False
self._session_plans: dict[str, list[dict[str, Any]]] = {}
self._session_cwds: dict[str, str] = {}
self._session_mcp_servers: dict[str, list[McpServer]] = {}
self._allowed_command_types: dict[
str, set[tuple[str, str | None]]
] = {} # Track allowed command types per session
def on_connect(self, conn: Client) -> None:View on GitHub (pinned to a1af029e6e)
Solutions
- Supply the model id: `anthropic:claude-sonnet-4-5`, not `anthropic:`
- Check the source of the model value (config key, env var) — it is empty or missing
- Use `ModelSpec.try_parse(...)` for non-raising validation
Example fix
// before
spec = ModelSpec.parse("anthropic:")
// after
spec = ModelSpec.parse("anthropic:claude-sonnet-4-5") Defensive patterns
Strategy: validation
Validate before calling
provider, _, model = raw.partition(":")
if not model.strip():
raise ValueError(f"spec {raw!r} is missing the model id")
spec = ModelSpec(provider=provider, model=model) Type guard
def is_valid_model_spec(obj: object) -> bool:
return isinstance(obj, ModelSpec) and bool(obj.provider) and bool(obj.model) Try / catch
try:
spec = ModelSpec.parse(raw)
except ValueError as exc:
logger.error("bad model spec %r: %s", raw, exc)
spec = DEFAULT_MODEL_SPEC Prevention
- Check config values for emptiness before interpolating them into spec strings
- Use `try_parse` for user-facing input paths
- Keep model ids in one config source so they cannot be silently dropped
When it happens
Trigger: Calling `ModelSpec(provider='anthropic', model='')`, `ModelSpec.parse('anthropic:')`, or `ModelSpec.parse('anthropic')`... (the latter hits parse's format error; the colon-only case hits this). Any spec string ending at the colon with no model text.
Common situations: Typing `provider:` in a config field with the model name forgotten; a truncated spec after copy/paste; empty value interpolated into a spec template like `f"{provider}:{model}"` where `model` came from missing config.
Related errors
- modes can only be provided when agent is a factory
- -32601
- -32602
- recursion_limit must be None or a positive integer
- Invalid class_path '{class_path}': must be in module.path:Cl
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/eb7b679dbc8c35d9.
Report an issue: GitHub.