langchain-ai/deepagents · error · ValueError
Async subagent '{name}' has no url configured. ASGI transpor
Error message
Async subagent '{name}' has no url configured. ASGI transport (url=None) requires async invocation. What it means
AsyncSubAgentsMiddleware's ClientManager raises this when get_sync() is asked for a sync LangGraph client for an async subagent whose spec has no 'url'. A spec without a url uses ASGI in-process transport, which only works with async clients; a sync client cannot be created. This guards call paths that synchronously poll subagent state (status checks, cancels, live fetches).
Source
Thrown at libs/deepagents/deepagents/middleware/async_subagents.py:216
class _ClientCache:
"""Lazily-created, cached Agent Protocol clients keyed by (url, headers)."""
def __init__(self, agents: dict[str, AsyncSubAgent]) -> None:
self._agents = agents
self._sync: dict[tuple[str | None, frozenset[tuple[str, str]]], SyncLangGraphClient] = {}
self._async: dict[tuple[str | None, frozenset[tuple[str, str]]], LangGraphClient] = {}
def _cache_key(self, spec: AsyncSubAgent) -> tuple[str | None, frozenset[tuple[str, str]]]:
"""Build a cache key from the agent spec's url and resolved headers."""
return (spec.get("url"), frozenset(_resolve_headers(spec).items()))
def get_sync(self, name: str) -> SyncLangGraphClient:
"""Get or create a sync client for the named agent."""
spec = self._agents[name]
if spec.get("url") is None:
msg = f"Async subagent '{name}' has no url configured. ASGI transport (url=None) requires async invocation."
raise ValueError(msg)
key = self._cache_key(spec)
if key not in self._sync:
self._sync[key] = get_sync_client(
url=spec.get("url"),
headers=_resolve_headers(spec),
)
return self._sync[key]
def get_async(self, name: str) -> LangGraphClient:
"""Get or create an async client for the named agent."""
spec = self._agents[name]
key = self._cache_key(spec)
if key not in self._async:
self._async[key] = get_client(
url=spec.get("url"),
headers=_resolve_headers(spec),
)
return self._async[key]View on GitHub (pinned to a1af029e6e)
Solutions
- Configure an httpx/HTTP URL in the subagent spec so sync polling is possible
- Use the async variants (await check_async_task/cancel_async_task) which use the ASGI-capable async client instead of get_sync
- Only start async tasks for url-backed subagents if you plan to sync-poll them
Example fix
// before
async_subagents=[{"name": "researcher", "graph": "researcher"}]
// after
async_subagents=[{"name": "researcher", "url": "http://localhost:8123"}] Defensive patterns
Strategy: validation
Validate before calling
def can_sync_poll(spec: dict) -> bool:
return spec.get("url") is not None Try / catch
try:
client = clients.get_sync(name)
except ValueError as e:
# fall back to async status path or skip polling
logger.warning("sync poll unavailable: %s", e) Prevention
- Always set a 'url' for subagents you intend to poll synchronously
- Use async tool variants for ASGI (url-less) subagents
- Validate specs at startup: every spec has either url or is only used async
When it happens
Trigger: Calling check_async_task, update_async_task, cancel_async_task, or _fetch_live_status (which call get_sync) for a subagent defined with no 'url' key (ASGI transport); get_sync itself is public so calling it directly with a url-less spec also triggers.
Common situations: Defining async subagents as in-process graphs (url omitted) and then having the agent synchronously poll or cancel the task; mixing sync tool paths with ASGI-transport subagents; typos like 'urls' or 'base_url' in the subagent spec.
Related errors
- allow_list must not be empty; disable shell access instead
- SHELL_ALLOW_ALL should not be used with ShellAllowListMiddle
- interpreter_ptc='all' exposes every host tool to PTC calls t
- Invalid interpreter_ptc string {ptc!r}; expected 'safe', 'al
- interpreter_ptc list entries cannot include 'all'; use 'all'
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/fea8d63c2be05eab.
Report an issue: GitHub.