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

  1. Run from the same working directory the thread was bound in (or pass the original cwd)
  2. Regenerate the context from the thread's persisted binding and send it unchanged
  3. 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

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


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/c926229e2f5c6939. Report an issue: GitHub.