{"record":{"id":"b4b9c3a4171c6ab9","repo":"oraios/serena","slug":"cannot-edit-external-file-relative-path","errorCode":null,"errorMessage":"Cannot edit external file: {relative_path}","messagePattern":"Cannot edit external file: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/serena/code_editor.py","lineNumber":83,"sourceCode":"        :param relative_path: the relative path of the file to read\n        :param lines: tuple of (first_line, last_line) to read only a specific line range (0-based, inclusive)\n        :return: the content of the file\n        \"\"\"\n        with self._open_file_context(relative_path) as file:\n            contents = file.get_contents()\n            if lines is None:\n                return contents\n            else:\n                first_line, last_line = lines\n                return TextUtils.get_text_in_lines_range(contents, first_line, last_line)\n\n    @contextmanager\n    def edited_file_context(self, relative_path: str) -> Iterator[\"CodeEditor.EditedFile\"]:\n        \"\"\"\n        Context manager for editing a file.\n        \"\"\"\n        if FileProxy.is_external_path(relative_path):\n            raise ValueError(f\"Cannot edit external file: {relative_path}\")\n        with self._open_file_context(relative_path) as edited_file:\n            yield edited_file\n            # save the file\n            self._save_edited_file(edited_file)\n\n    def _save_edited_file(self, edited_file: \"CodeEditor.EditedFile\") -> None:\n        abs_path = os.path.join(self.project_root, edited_file.relative_path)\n        new_contents = edited_file.get_contents()\n        with open(abs_path, \"w\", encoding=self.encoding, newline=self.newline) as f:\n            f.write(new_contents)\n\n    @abstractmethod\n    def _find_unique_symbol(self, name_path: str, relative_file_path: str) -> TSymbol:\n        \"\"\"\n        Finds the unique symbol with the given name in the given file.\n        If no such symbol exists, raises a ValueError.\n\n        :param name_path: the name path","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/oraios/serena/blob/7fcbca7e62555ec2287ddb2f083caee805848ea6/src/serena/code_editor.py#L65-L101","documentation":"edited_file_context refuses to open files outside the project root. FileProxy.is_external_path detects relative paths that escape the project (e.g. via `..`) and raises ValueError, since the editor only manages files within its project root.","triggerScenarios":"Calling edited_file_context (or its callers: replace_body, insert_after/before_symbol, insert_at_line, delete_lines, delete_symbol) with a path like `../other/file.py`, an absolute path outside the root, or a symlink-resolved external path.","commonSituations":"Tool/LLM-supplied paths pointing at files outside the project; scripts operating on sibling directories; path traversal from user input; accidentally passing absolute paths to an editor rooted at the project.","solutions":["Pass a path relative to the project root, without `..` segments.","Copy the external file into the project if it must be edited with this editor.","Use a separate CodeEditor instance rooted at the other project/directory.","Validate/normalize user-supplied paths against the project root before editing."],"exampleFix":"// before\nwith editor.edited_file_context(\"../shared/util.py\") as f: ...\n// after\nwith editor.edited_file_context(\"src/shared/util.py\") as f: ...","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef is_inside_project(rel: str, root: Path) -> bool:\n    p = (root / rel).resolve()\n    return p.is_relative_to(root.resolve()) and p.is_file()\n\nif not is_inside_project(user_path, project_root):\n    raise ValueError(f'{user_path} is outside the project; refusing to edit')","typeGuard":null,"tryCatchPattern":"try:\n    with editor.edited_file_context(rel_path) as f:\n        f.replace_body(...)\nexcept ValueError as e:\n    if str(e).startswith('Cannot edit external file'):\n        print(f'{rel_path} must be inside the project root')","preventionTips":["Always pass project-root-relative paths without `..` segments.","Sanitize LLM/user-supplied paths against the project root before editing.","Use a separate editor instance rooted elsewhere for out-of-project files."],"tags":["path-traversal","validation","code-editor","security"],"backgroundTag":"path-outside-project-root","analyzedSha":"7fcbca7e62555ec2287ddb2f083caee805848ea6","analyzedAt":"2026-08-29T00:04:09.619Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}