langchain-ai/deepagents · error · WorkspaceConflictError
workspace context does not match thread {thread_id}
Error message
workspace context does not match thread {thread_id} What it means
`require_thread_workspace._require` compares each key in the persisted binding's payload with the claimed context data. This `WorkspaceConflictError` is raised when any field of the provided workspace context (e.g. resource key, cwd, policy) does not equal the persisted binding, meaning the run claims a different workspace than the thread is bound to.
Source
Thrown at libs/code/deepagents_code/workspace.py:363
_, claimed_fingerprint = canonical_workspace_config(workspace_config)
def _require() -> WorkspaceBinding:
with sqlite3.connect(_database_path(), timeout=5) as conn:
conn.row_factory = sqlite3.Row
conn.execute("BEGIN IMMEDIATE")
_initialize(conn)
row = conn.execute(
"SELECT * FROM dcode_thread_workspaces WHERE thread_id = ?",
(thread_id,),
).fetchone()
if row is None:
msg = f"thread {thread_id} has no workspace binding"
raise WorkspaceConflictError(msg)
existing = _row_binding(row)
expected = existing.to_payload()
if any(data.get(key) != value for key, value in expected.items()):
msg = f"workspace context does not match thread {thread_id}"
raise WorkspaceConflictError(msg)
if (
claimed_fingerprint is not None
and claimed_fingerprint != existing.config_fingerprint
):
msg = f"workspace configuration does not match thread {thread_id}"
raise WorkspaceConflictError(msg)
return existing
existing = await asyncio.to_thread(_require)
if existing.schema_version != _SCHEMA_VERSION:
msg = f"workspace binding schema is unsupported for thread {thread_id}"
raise WorkspaceConflictError(msg)
resolved = await asyncio.to_thread(
resolve_workspace,
existing.cwd,
existing.workspace_config(),
config_fingerprint=existing.config_fingerprint,
)View on GitHub (pinned to a1af029e6e)
Solutions
- Run from the same working directory the thread was bound in (or pass the original cwd)
- Regenerate the context from the thread's persisted binding and send it unchanged
- If the move is intentional, rebind under a new thread id
Example fix
// before
require_thread_workspace(tid, payload={**ctx, "cwd": "/elsewhere"})
// after
require_thread_workspace(tid, payload=ctx) # ctx from binding.to_payload() Defensive patterns
Strategy: validation
Validate before calling
# regenerate context from the persisted binding instead of stale copies binding = await require_thread_workspace(thread_id, payload=None) if False else None ctx = binding.to_payload() # always source context from the binding
Try / catch
try:
await require_thread_workspace(tid, payload=ctx)
except WorkspaceConflictError as exc:
if "does not match" in str(exc):
os.chdir(ctx["cwd"]) # or restart from the bound cwd
await require_thread_workspace(tid, payload=ctx) Prevention
- Launch runs from the same cwd used at bind time
- Never copy contexts between threads
- Canonicalize paths (resolve symlinks) before comparing
When it happens
Trigger: Running a thread from a different working directory than at bind time; a workspace context payload built from a different `resolve_workspace` result; tampered or stale context forwarded from another session.
Common situations: Launching the agent from a different shell cwd (or a symlinked path that canonicalizes differently); copying run payloads between threads; upgrading machines where absolute paths changed.
Related errors
- thread {thread_id} is already bound to a different workspace
- thread {thread_id} has no workspace binding
- workspace configuration does not match thread {thread_id}
- workspace binding schema is unsupported for thread {thread_i
- workspace identity changed for thread {thread_id}
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/c926229e2f5c6939.
Report an issue: GitHub.