{"record":{"id":"381047574a2b111f","repo":"oraios/serena","slug":"memory-name-cannot-contain-segments-got-na","errorCode":null,"errorMessage":"Memory name cannot contain '..' segments. Got: {name}","messagePattern":"Memory name cannot contain '\\.\\.' segments\\. Got: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/serena/memories/memory_manager.py","lineNumber":178,"sourceCode":"        resolution): directory symlinks placed inside the memories folder are a supported way to\n        share memories (e.g. a monorepo symlinking each submodule's memory dir), and those must\n        keep resolving to their targets at I/O time.\n        \"\"\"\n        filename = f\"{parts[-1]}.md\"\n        subdir = base_dir if len(parts) == 1 else base_dir.joinpath(*parts[:-1])\n        candidate = subdir / filename\n        base_norm = Path(os.path.normpath(base_dir))\n        if not Path(os.path.normpath(candidate)).is_relative_to(base_norm):\n            raise ValueError(f\"Memory name resolves outside the memories directory. Got: {'/'.join(parts)}\")\n        subdir.mkdir(parents=True, exist_ok=True)\n        return candidate\n\n    def get_memory_file_path(self, name: str) -> Path:\n        name = self._sanitize_name(name)\n        parts = name.split(\"/\")\n\n        if \"..\" in parts:\n            raise ValueError(f\"Memory name cannot contain '..' segments. Got: {name}\")\n\n        # Reject absolute names and empty path segments: pathlib discards the base directory when\n        # joined with an absolute path (e.g. \"/etc/cron.d/backdoor\" would reset to \"/etc/cron.d\"),\n        # letting a memory name escape the sandbox. A leading \"/\" produces an empty first segment.\n        if os.path.isabs(name) or \"\" in parts:\n            raise ValueError(f\"Memory name cannot be absolute or contain empty path segments. Got: {name}\")\n\n        if self._is_global(name):\n            if name == self.GLOBAL_TOPIC:\n                raise ValueError(\n                    f'Bare \"{self.GLOBAL_TOPIC}\" is not a valid memory name. Use \"{self.GLOBAL_TOPIC}/<name>\" to address a global memory.'\n                )\n            # Strip \"global/\" prefix and resolve against global dir\n            sub_name = name[len(self.GLOBAL_TOPIC) + 1 :]\n            return self._resolve_memory_path(self._global_memory_dir, sub_name.split(\"/\"))\n\n        # Project-local memory\n        assert self._project_memory_dir is not None, \"Project dir was not passed at initialization\"","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/oraios/serena/blob/7fcbca7e62555ec2287ddb2f083caee805848ea6/src/serena/memories/memory_manager.py#L160-L196","documentation":"get_memory_file_path explicitly rejects memory names containing '..' segments with a ValueError before resolving the path. This is a path-traversal guard: '..' would let a memory name address files outside the .serena/memories sandbox.","triggerScenarios":"Calling load_memory, save_memory, delete_memory, move_memory, edit_memory, or get_memory_file_path with a name like \"../config\" or \"topic/../../secrets\" (the '..' check precedes the absolute/empty-segment check).","commonSituations":"LLM agents hallucinating relative-style memory names; users trying to read files outside the project memories dir through the memory tool; scripting that interpolates user input into memory names.","solutions":["Remove '..' segments and address the memory by its name within the memories directory.","If the target file lives outside memories, use the read_file tool on its absolute path instead.","Validate or normalize names from untrusted sources before passing them to memory APIs (reject any name containing '..').","Note names are sanitized first ('mem:' prefix, '.md' suffix, OS separators removed) — after sanitization '..' still means a literal dot-dot segment and is invalid."],"exampleFix":"// before\nmanager.load_memory(\"../secrets/keys\")\n// after\nmanager.load_memory(\"secrets/keys\")  # stays inside .serena/memories","handlingStrategy":"validation","validationCode":"if \"..\" in name.split(\"/\"):\n    raise ValueError(f\"memory name must not contain '..': {name!r}\")","typeGuard":"def has_no_dotdot(name: str) -> bool:\n    return \"..\" not in name.split(\"/\")","tryCatchPattern":"try:\n    content = manager.load_memory(name)\nexcept ValueError as e:\n    if \"'..'\" in str(e):\n        log.warning(\"traversal blocked for %r; use read_file for external files\", name)\n    else:\n        raise","preventionTips":["Reject or normalize '..' in names at your API boundary","For files outside memories, use read_file on absolute paths, never memory names","Audit LLM-generated names before passing them to memory APIs"],"tags":["python","path-traversal","security","validation"],"backgroundTag":"path-traversal-rejected","analyzedSha":"7fcbca7e62555ec2287ddb2f083caee805848ea6","analyzedAt":"2026-08-29T00:04:09.619Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}