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

  1. Use a plain memory name (letters, digits, hyphens, optional topic/ subpath) that stays inside the memories directory.
  2. Remove traversal or unusual path segments from the name before calling memory APIs.
  3. If you need content stored elsewhere, do not route it through memories — use read_file/write_file on the absolute path directly.
  4. 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

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


AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29). Data as JSON: /api/errors/1f43b3536af48d92. Report an issue: GitHub.