shareAI-lab/learn-claude-code · error · ValueError
Task store escapes the workspace
Error message
Task store escapes the workspace
What it means
Raised by TaskStore._root() in s10_task_system/code.py:85 when the store directory, after resolve(), is not inside the resolved WORKDIR (Path.cwd() at import time). Task JSON files must live inside the workspace as a containment guarantee; if the configured directory resolves elsewhere (symlink, absolute path outside cwd), every store operation refuses to proceed. The check runs on all path access, optionally creating the directory first.
Source
Thrown at s10_task_system/code.py:85
class Task:
id: str
subject: str
description: str
status: str
owner: str | None
blockedBy: list[str]
class TaskStore:
def __init__(self, directory: Path):
self.directory = directory
def _root(self, create: bool = False) -> Path:
if create:
self.directory.mkdir(parents=True, exist_ok=True)
root = self.directory.resolve()
if not root.is_relative_to(WORKDIR.resolve()):
raise ValueError("Task store escapes the workspace")
return root
def _path(self, task_id: str, create_root: bool = False) -> Path:
if not isinstance(task_id, str) or not TASK_ID_PATTERN.fullmatch(task_id):
raise ValueError(f"Invalid task ID: {task_id!r}")
root = self._root(create=create_root)
path = (root / f"{task_id}.json").resolve()
if not path.is_relative_to(root):
raise ValueError(f"Invalid task ID: {task_id!r}")
return path
def exists(self, task_id: str) -> bool:
return self._path(task_id).is_file()
def create(self, subject: str, description: str = "",
blocked_by: list[str] | None = None) -> Task:
subject = subject.strip()
if not subject:View on GitHub (pinned to 985456f4ad)
Solutions
- Construct the store inside the workspace: TaskStore(WORKDIR / '.tasks') or any path that resolves under Path.cwd().
- In tests, run in a tmp cwd (monkeypatch.chdir(tmp_path)) and construct TaskStore(tmp_path / '.tasks') so both share the same root, or reload the module so WORKDIR is recomputed.
- Replace symlinks pointing outside the workspace with a real directory inside it; if sharing is required, place the store in the common parent and run the tool from there.
- Verify with .resolve().is_relative_to(Path.cwd().resolve()) before constructing the store.
Example fix
# before (fails when cwd is the project root)
store = TaskStore(Path('/home/user/tasks'))
# after
from s10_task_system.code import WORKDIR
store = TaskStore(WORKDIR / '.tasks') Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
def store_dir_is_safe(directory: Path, workdir: Path) -> bool:
return directory.resolve().is_relative_to(workdir.resolve()) Try / catch
try:
store = TaskStore(directory)
tasks = store.list()
except ValueError as e:
if 'escapes the workspace' in str(e):
raise SystemExit('task store must live inside the workspace')
raise Prevention
- Always construct the store as WORKDIR / '.tasks'.
- In tests, chdir into a tmp workspace before importing the module so WORKDIR matches the store.
- Never symlink the store outside the workspace; share stores by running from a common parent.
When it happens
Trigger: Constructing TaskStore(Path('/etc/tasks')) or any absolute directory outside the current working directory; passing a relative directory that traverses out of the workspace such as Path('../shared_tasks'); a symlinked .tasks directory pointing to another location; running the module with a different cwd than expected, since WORKDIR is frozen at import.
Common situations: Tests that create TaskStore in a tmp_path outside the (separately computed) workspace root; CLI tools invoked from a subdirectory so Path.cwd() differs from the project root; users trying to share one task store across multiple project checkouts via a symlink.
Related errors
- Memory path escapes the store: {filename}
- Could not allocate a unique task ID
- Tasks directory escapes workspace
- Path escapes workspace: {path}
- Path escapes workspace: {p}
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/a7500c04d9a8480e.
Report an issue: GitHub.