shareAI-lab/learn-claude-code · error · ValueError
Memory directory escapes the workspace
Error message
Memory directory escapes the workspace
What it means
Raised by memory_path() in s09_memory/code.py as a startup/configuration sanity check: MEMORY_DIR.resolve() must lie inside WORKDIR.resolve(), or every memory operation aborts. It is an environment invariant, not user-input dependent — if it fires, the memory directory was mounted/placed outside the agent sandbox, and the module refuses rather than write outside the workspace. This fires before any filename logic, so all memory tools fail uniformly.
Source
Thrown at s09_memory/code.py:96
except yaml.YAMLError:
return {}, text
if not isinstance(metadata, dict):
return {}, text
return metadata, parts[2].lstrip()
def memory_slug(name: str) -> str:
slug = re.sub(r"[^\w]+", "-", name.lower()).strip("-_")
return slug or "memory"
def memory_path(filename: str, allow_index: bool = False) -> Path:
if Path(filename).name != filename:
raise ValueError(f"Invalid memory filename: {filename}")
if filename == MEMORY_INDEX.name and not allow_index:
raise ValueError("The memory index is not a memory record")
root = MEMORY_DIR.resolve()
if not root.is_relative_to(WORKDIR.resolve()):
raise ValueError("Memory directory escapes the workspace")
path = (root / filename).resolve()
if not path.is_relative_to(root):
raise ValueError(f"Memory path escapes the store: {filename}")
return path
def _memory_slug(name: str) -> str:
return memory_slug(name)
def _normalized_memory_text(value: str) -> str:
return " ".join(value.lower().split())
def should_store_memory(candidate: dict, existing: list[dict]) -> bool:
"""Accept durable records that are not temporary or already stored."""
if not isinstance(candidate, dict):
return False
if candidate.get("scope") != "persistent":
return False
if candidate.get("type") not in MEMORY_TYPES:View on GitHub (pinned to 985456f4ad)
Solutions
- Place the memory store inside the workspace, e.g. WORKDIR / '.agent' / 'memory', and update the configuration
- If an outside location is truly required, extend WORKDIR to a common ancestor containing both — the module will not allow bypassing this
- Check for symlinks on MEMORY_DIR/WORKDIR and use real paths in configuration so resolve() is stable
Example fix
# before (env) MEMORY_DIR=/var/lib/agent/memory # outside WORKDIR # after MEMORY_DIR=<WORKDIR>/.agent/memory # inside WORKDIR
Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
wd = WORKDIR.resolve()
md = MEMORY_DIR.resolve()
assert md.is_relative_to(wd), (
f"MEMORY_DIR {md} must live inside WORKDIR {wd}; "
f"set it to {wd / '.agent' / 'memory'}"
) Prevention
- Keep the memory store inside the workspace directory
- Use resolved real paths for WORKDIR/MEMORY_DIR configuration to avoid symlink surprises
- Run this containment assertion at startup, before any memory tool is registered
When it happens
Trigger: MEMORY_DIR configured (or defaulted) to something like ~/.memory or /var/data/memory while WORKDIR is the project directory; MEMORY_DIR or WORKDIR being a symlink whose target resolves outside the expected tree.
Common situations: Env var pointing the memory store at a shared location outside the workspace; project moved so a previously-inside relative path now resolves elsewhere; symlinked home/project directories changing resolve() outcomes across machines.
Related errors
- Path escapes workspace: {p}
- Path escapes workspace: {p}
- Path escapes workspace: {p}
- Path escapes workspace: {p}
- Path escapes workspace: {p}
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/1614e9b9eea18da4.
Report an issue: GitHub.