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.
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
- Invalid memory filename
- MODEL_ID is required in the environment or .env
- path escapes the current repository
- Path escapes workspace
- Path escapes workspace
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/1614e9b9eea18da4.
Report an issue: GitHub.
Appendix: 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)