oraios/serena · error · ValueError
Memory name resolves outside the memories directory. Got: {'
Error message
Memory name resolves outside the memories directory. Got: {'/'.join(parts)} What it means
_resolve_memory_path normalizes the candidate path and checks it stays inside the memories base directory (relative_to). If a crafted memory name resolves outside that directory (e.g. via symlink-like or normalization tricks), ValueError is raised. It is a sandbox guard preventing path escape in memory file resolution.
Source
Thrown at src/serena/memories/memory_manager.py:169
"""
Builds the ``*.md`` path for ``parts`` under ``base_dir``, creating any parent
subdirectories, and guarantees the result stays inside ``base_dir``.
The containment check is a defense-in-depth backstop for :meth:`get_memory_file_path`'s
up-front segment validation: even if a crafted name slipped through, the built path must
never escape the memories sandbox (which would let an agent read/write/delete arbitrary
files). The check runs *before* any directory is created, so a rejected name cannot leave
stray directories behind either. It is deliberately *lexical* (``normpath``, no symlink
resolution): directory symlinks placed inside the memories folder are a supported way to
share memories (e.g. a monorepo symlinking each submodule's memory dir), and those must
keep resolving to their targets at I/O time.
"""
filename = f"{parts[-1]}.md"
subdir = base_dir if len(parts) == 1 else base_dir.joinpath(*parts[:-1])
candidate = subdir / filename
base_norm = Path(os.path.normpath(base_dir))
if not Path(os.path.normpath(candidate)).is_relative_to(base_norm):
raise ValueError(f"Memory name resolves outside the memories directory. Got: {'/'.join(parts)}")
subdir.mkdir(parents=True, exist_ok=True)
return candidate
def get_memory_file_path(self, name: str) -> Path:
name = self._sanitize_name(name)
parts = name.split("/")
if ".." in parts:
raise ValueError(f"Memory name cannot contain '..' segments. Got: {name}")
# Reject absolute names and empty path segments: pathlib discards the base directory when
# joined with an absolute path (e.g. "/etc/cron.d/backdoor" would reset to "/etc/cron.d"),
# letting a memory name escape the sandbox. A leading "/" produces an empty first segment.
if os.path.isabs(name) or "" in parts:
raise ValueError(f"Memory name cannot be absolute or contain empty path segments. Got: {name}")
if self._is_global(name):
if name == self.GLOBAL_TOPIC:View on GitHub (pinned to 7fcbca7e62)
Solutions
- Use a plain memory name (letters, digits, hyphens, optional topic/ subpath) that stays inside the memories directory.
- Remove traversal or unusual path segments from the name before calling memory APIs.
- If you need content stored elsewhere, do not route it through memories — use read_file/write_file on the absolute path directly.
- Sanitize/validate externally supplied names (e.g. with a regex like ^[a-zA-Z0-9_\-/]+$) before passing them in.
Example fix
// before
manager.load_memory("../other_project/notes")
// after
manager.load_memory("notes") # or "topic/notes", resolved inside memories dir Defensive patterns
Strategy: validation
Validate before calling
import re
if not re.fullmatch(r"[A-Za-z0-9_][A-Za-z0-9_\-/]*", name):
raise ValueError(f"unsafe memory name: {name!r}") Type guard
def is_safe_memory_name(name: str) -> bool:
import re
return bool(re.fullmatch(r"[A-Za-z0-9_][A-Za-z0-9_\-/]*", name)) Try / catch
try:
path = manager.get_memory_file_path(name)
except ValueError as e:
if "outside the memories directory" in str(e):
log.error("rejected escaping memory name: %s", name)
else:
raise Prevention
- Never build memory names from raw user/LLM input without a whitelist regex
- Keep names as simple topic/subtopic identifiers
- Treat any normalization outside the base dir as a security signal
When it happens
Trigger: get_memory_file_path with a name whose parts, after joining to base_dir and normpath, escape the memories directory — typically names combining segments that climb out (beyond the explicit '..' check) or otherwise normalize outside the base.
Common situations: Programmatic callers constructing memory names from untrusted input; names containing many traversal segments or unusual separators; attempts to point a memory at an arbitrary filesystem location.
Related errors
- Memory name cannot contain '..' segments. Got: {name}
- Memory name cannot be absolute or contain empty path segment
- Cannot edit external file: {relative_path}
- Cannot use both fixed_tools and excluded_tools/included_opti
- Unknown language backend '{backend_str}': valid values are {
AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29).
Data as JSON: /api/errors/1f43b3536af48d92.
Report an issue: GitHub.