langchain-ai/deepagents · error · ValueError
thread_id is required in config.configurable
Error message
thread_id is required in config.configurable
What it means
RemoteAgent methods (`aoffload`, `astream`, `aget_state`, `acancel_active_runs`, `aupdate_state`, `aabandon_pending_work`) require a LangGraph thread id to know which server-side thread to operate on. `_require_thread_id` extracts `config['configurable']['thread_id']` and raises ValueError when the config is None, missing `configurable`, or has an empty/falsy `thread_id`. The thread id is mandatory because every underlying call targets `/threads/{thread_id}/...` on the server.
Source
Thrown at libs/code/deepagents_code/client/remote_client.py:166
return cast("OffloadResult", result)
def _require_thread_id(config: Mapping[str, Any] | None) -> str:
"""Extract and validate that `thread_id` is present in config.
Args:
config: Config dict with `configurable.thread_id`.
Returns:
The thread ID string.
Raises:
ValueError: If `thread_id` is missing.
"""
thread_id = (config or {}).get("configurable", {}).get("thread_id")
if not thread_id:
msg = "thread_id is required in config.configurable"
raise ValueError(msg)
return thread_id
def state_has_pending_work(state: object) -> bool:
"""Return whether a checkpoint snapshot still holds unfinished graph work.
Single definition of "pending" shared by the app-side detector and the
post-recovery verification, so the two cannot drift apart.
Args:
state: A `StateSnapshot`-shaped object, or `None`.
Returns:
Whether the snapshot has a queued node, task, or interrupt.
"""
if state is None:
return False
return bool(View on GitHub (pinned to a1af029e6e)
Solutions
- Build the config as `{'configurable': {'thread_id': <id>}}` with a non-empty string id before calling any RemoteAgent method.
- If the thread id comes from a checkpointer or session manager, copy its config (`config.copy()` keeps `configurable`) rather than constructing a new dict.
- Validate the config early in your own wrapper: check `config.get('configurable', {}).get('thread_id')` and fail with a clear message at your boundary.
- If you have no thread id yet, create/register the thread first (e.g. via the server's thread APIs or `aensure_thread`) and then pass its id.
Example fix
// before
await agent.astream(input, config={})
// after
config = {"configurable": {"thread_id": thread_id}}
await agent.astream(input, config=config) Defensive patterns
Strategy: validation
Validate before calling
def has_thread_id(config) -> bool:
return bool((config or {}).get("configurable", {}).get("thread_id"))
if not has_thread_id(config):
raise ValueError("config.configurable.thread_id must be set before calling RemoteAgent") Type guard
def is_runnable_config(config: object) -> bool:
return (
isinstance(config, dict)
and isinstance(config.get("configurable"), dict)
and isinstance(config["configurable"].get("thread_id"), str)
and bool(config["configurable"]["thread_id"])
) Try / catch
try:
await agent.astream(input, config=config)
except ValueError as exc:
if "thread_id is required" in str(exc):
logging.error("No thread id in config.configurable; create/register a thread first")
else:
raise Prevention
- Always derive configs from the checkpointer/framework (`config.copy()`) instead of building fresh dicts.
- Add a tiny helper that constructs `{"configurable": {"thread_id": id}}` and use it everywhere.
- Assert thread id presence in tests that exercise RemoteAgent calls.
When it happens
Trigger: Calling any RemoteAgent method with `config=None`, `config={}`, `config={'configurable': {}}`, or `config={'configurable': {'thread_id': ''}}` (or None).
Common situations: Forgetting to thread the checkpointer-assigned thread id through a LangGraph `RunnableConfig`; constructing a fresh config dict by hand instead of copying one the framework produced; building a client wrapper that drops `configurable` when it sanitizes configs; passing a config from a non-LangGraph code path that uses different keys.
Related errors
- modes can only be provided when agent is a factory
- models can only be provided when agent is a factory
- -32602
- recursion_limit must be None or a positive integer
- Invalid MCP config at {mcp_config_path}: {exc}
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/010ba07fe2016ee0.
Report an issue: GitHub.