shareAI-lab/learn-claude-code · error · ValueError
Assignment cwd changed for task {task.id}
Error message
Assignment cwd changed for task {task.id} What it means
After confirming an assignment's task is active, the harness recomputes the task's worktree cwd and compares it (resolved) to the cwd recorded when the lease was granted. A mismatch means the on-disk worktree location for that task changed under the lease — e.g. the worktree was recreated at a different path, or the recorded cwd pointed at a since-moved directory. Continuing would run commands in the wrong directory, so it aborts.
Source
Thrown at s15_integrated_harness/code.py:482
with task_lock:
assignment = teammate_assignments.get(owner)
task = _owner_in_progress(owner)
if task and (not assignment or assignment.get("task_id") != task.id):
cwd, error = task_worktree_cwd(task)
if error:
raise ValueError(error)
assignment = {"task_id": task.id, "cwd": cwd}
teammate_assignments[owner] = assignment
elif not assignment:
return WORKDIR
task = load_task(str(assignment["task_id"]))
if task.status not in {"in_progress", "completed"} or task.owner != owner:
raise ValueError(f"Assignment for {owner} is no longer active")
cwd, error = task_worktree_cwd(task)
if error:
raise ValueError(error)
if cwd.resolve() != Path(assignment["cwd"]).resolve():
raise ValueError(f"Assignment cwd changed for task {task.id}")
return cwd
def release_completed_assignment(owner: str) -> bool:
"""Release a completed cwd lease only at a model turn boundary."""
with task_lock:
assignment = teammate_assignments.get(owner)
if not assignment:
return False
task = load_task(str(assignment["task_id"]))
if task.status != "completed" or task.owner != owner:
return False
teammate_assignments.pop(owner, None)
advance_assignment_version(owner)
if owner in globals().get("plan_gates", {}):
globals()["plan_gates"][owner] = "not_required"
return True
View on GitHub (pinned to 985456f4ad)
Solutions
- Do not remove/recreate a worktree while its task is in_progress; finish or reassign the task first.
- Restart the harness after moving the workspace so absolute cwds in assignments are regenerated.
- Change a task's worktree only through the harness APIs so the lease updates atomically.
Defensive patterns
Strategy: try-catch
Validate before calling
from pathlib import Path
def worktree_stable(task, recorded_cwd: str) -> bool:
cwd, err = task_worktree_cwd(task)
if err:
return False
return cwd.resolve() == Path(recorded_cwd).resolve() Try / catch
try:
cwd = _agent_cwd_for(owner)
except ValueError as e:
if "cwd changed" in str(e):
# worktree was moved/recreated: re-establish the lease via restart
raise Prevention
- Never prune/recreate a worktree whose task is in_progress.
- Restart the harness after relocating the workspace.
- Change worktree bindings only through harness APIs.
When it happens
Trigger: Deleting and recreating a task's worktree under a different name/path while its owner holds a lease; task.worktree field edited between turns; WORKTREES_DIR restructured; the recorded absolute cwd became stale after moving the whole workspace.
Common situations: Agents running 'git worktree prune/remove + add' on an in-use worktree; moving or cloning the workspace to a new path without restarting sessions; hand-editing task JSON's worktree field.
Related errors
- Assignment for {owner} is no longer active
- Assignment for {owner} is no longer active
- Task {task_id} not found
- Invalid status: {status}
- Not in a git repository. worktree tools require git.
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/fed621f3632401aa.
Report an issue: GitHub.