oraios/serena · error · ValueError

{relative_path=} points outside the project root ({self.proj

Error message

{relative_path=} points outside the project root ({self.project_root})

What it means

Raised by Project.validate_relative_path (src/serena/project.py:341) when a given relative path escapes the project root — i.e. is_path_in_project() is False. It prevents file operations (apply, file collection) from touching anything outside the project sandbox.

Source

Thrown at src/serena/project.py:341

        if require_file:
            return exists and abs_path.is_file()
        else:
            return exists

    def validate_relative_path(self, relative_path: str, require_not_ignored: bool = False) -> None:
        """
        Validates that the given relative path is within the project directory
        (and, optionally, not ignored according to the project's ignore settings),
        raising a ValueError if the validation fails.

        :param relative_path: the path to validate, relative to the project root
        :param require_not_ignored: if True, the path must not be ignored according to the project's ignore settings
        """
        if FileProxy.is_external_path(relative_path):
            return

        if not self.is_path_in_project(relative_path):
            raise ValueError(f"{relative_path=} points outside the project root ({self.project_root})")

        if require_not_ignored:
            if self.is_ignored_path(relative_path):
                raise ValueError(f"Path {relative_path} is ignored")

    def gather_source_files(self, relative_path: str = "") -> list[str]:
        """Retrieves relative paths of all source files, optionally limited to the given path

        :param relative_path: if provided, restrict search to this path
        """
        rel_file_paths = []
        start_path = os.path.join(self.project_root, relative_path)
        if not os.path.exists(start_path):
            raise FileNotFoundError(f"Relative path {start_path} not found.")
        if os.path.isfile(start_path):
            return [relative_path]
        else:
            for root, dirs, files in os.walk(start_path, followlinks=True):

View on GitHub (pinned to 7fcbca7e62)

Solutions

  1. Pass a path relative to the project root with no '..' components
  2. If you intentionally need an outside file, use the external-path API (FileProxy external paths) instead of a project-relative path
  3. Normalize the path (os.path.normpath) and check it stays under project_root before calling

Example fix

// before
project.validate_relative_path('../shared/config.py')
// after
rel = os.path.relpath('/abs/shared/config.py', project.project_root)
if not rel.startswith('..'):
    project.validate_relative_path(rel)
Defensive patterns

Strategy: validation

Validate before calling

rel = os.path.normpath(rel)
assert not rel.startswith('..') and not os.path.isabs(rel), 'path escapes project root'

Try / catch

try:
    project.validate_relative_path(rel, require_not_ignored=True)
except ValueError as e:
    logging.error('Invalid project-relative path: %s', e)
    return None

Prevention

When it happens

Trigger: Passing paths containing '..' components, absolute paths not under project_root (and not flagged as external), or symlinks resolving outside the project to validate_relative_path, apply, or _collect_files.

Common situations: Agents constructing '../other-repo/file.py' style paths; joining user-supplied absolute paths onto project_root; symlinked directories pointing outside the repo.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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