langchain-ai/deepagents · error · ValueError
MCPServerInfo {self.name!r}: status='ok' cannot carry an err
Error message
MCPServerInfo {self.name!r}: status='ok' cannot carry an error (got {self.error!r}) What it means
MCPServerInfo's __post_init__ enforces a consistent status/error pairing: a server reporting status='ok' succeeded and must not also carry an error message. Constructing an MCPServerInfo with status='ok' and a non-None error fails this invariant with a ValueError.
Source
Thrown at libs/code/deepagents_code/mcp_tools.py:178
already covered by `needs_attention()`.
"""
def __post_init__(self) -> None:
"""Enforce the status/error/tools consistency invariant.
Raises:
ValueError: If any of: `status='ok'` with a non-`None` error;
non-`ok` status without an error message; non-`ok` status
carrying tools; or `pending_reconnect` set without
`status='disabled'`.
"""
if self.status == "ok":
if self.error is not None:
msg = (
f"MCPServerInfo {self.name!r}: status='ok' cannot carry "
f"an error (got {self.error!r})"
)
raise ValueError(msg)
else:
if self.error is None:
msg = (
f"MCPServerInfo {self.name!r}: status={self.status!r} "
"requires an error message"
)
raise ValueError(msg)
if self.tools:
msg = (
f"MCPServerInfo {self.name!r}: status={self.status!r} "
"cannot carry tools"
)
raise ValueError(msg)
if self.pending_reconnect and self.status != "disabled":
msg = (
f"MCPServerInfo {self.name!r}: pending_reconnect requires "
f"status='disabled' (got {self.status!r})"
)View on GitHub (pinned to a1af029e6e)
Solutions
- Set error=None when status is 'ok'; keep the message only for failure statuses.
- If you want to surface a non-fatal warning, encode it in a dedicated field or in tools metadata, not in `error`.
- Fix fixture/adapter code that copies `error` across status transitions.
Example fix
// before MCPServerInfo(name="github", status="ok", error="timeout earlier") // after MCPServerInfo(name="github", status="ok", error=None)
Defensive patterns
Strategy: type-guard
Validate before calling
def make_server_info(name: str, status: str, error: str | None = None, tools: list | None = None) -> MCPServerInfo:
if status == "ok" and error is not None:
raise ValueError("status='ok' cannot carry an error")
return MCPServerInfo(name=name, status=status, error=error, tools=tools or []) Type guard
def is_consistent(info: MCPServerInfo) -> bool:
return (info.error is None) if info.status == "ok" else (info.error is not None) Try / catch
try:
info = MCPServerInfo(name=name, status="ok", error=err)
except ValueError as exc:
print(f"dropping inconsistent server info: {exc}")
info = MCPServerInfo(name=name, status="ok", error=None) Prevention
- Set error=None on success paths — don't carry stale errors across retries
- Build MCPServerInfo through one helper/factory, not ad hoc literals
- When reusing a failed info as a template, explicitly clear error/tools
When it happens
Trigger: Programmatically constructing `MCPServerInfo(name=..., status='ok', error=<message>)` in code that aggregates MCP server health (e.g. custom tooling or tests around mcp_tools listing).
Common situations: Adapters wrapping third-party results that set both a success status and a residual error string; refactors that populate error unconditionally; test fixtures copying fields from a failed server info and only changing status.
Related errors
- MCPServerInfo {self.name!r}: status={self.status!r} requires
- ConfigResolution must have at least one used path
- {self.scope} MCP config {need} a project root: {self.path}
- ColdCacheWarning age_seconds={self.age_seconds!r} does not p
- `unreadable_reason` implies the token is not usable; `logged
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/7a14455d61b91785.
Report an issue: GitHub.