oraios/serena · error · FileNotFoundError

Relative path {relative_path} does not exist.

Error message

Relative path {relative_path} does not exist.

What it means

_collect_files (used by replace_content's scope collection) validates that the given relative path exists under the project root after ignore-rule validation. If it does not exist, a FileNotFoundError is raised before any matching or replacement happens.

Source

Thrown at src/serena/tools/file_tools.py:327

            )
        ambiguous = [o for o in occurrences if o.is_ambiguous]
        if ambiguous:
            listing = self._render_listing(replacer, occurrences, contents, max_answer_chars, dry_run=False)
            raise ValueError(
                f"{len(ambiguous)} occurrence(s) are ambiguous (the pattern matches again inside the matched text, "
                f"indicating possible over-matching) - NO changes were applied. Review the prospective changes below "
                f"and either refine the pattern or explicitly select occurrences via occurrence_ids.\n{listing}"
            )
        return self._apply_occurrences(replacer, occurrences, contents, needle, repl)

    def _collect_files(self, relative_path: str, paths_include_glob: str, paths_exclude_glob: str) -> list[tuple[str, str]]:
        """Collects (relative_path, content) pairs of the non-ignored files in scope, in sorted path order."""
        relative_path = relative_path.strip()
        if relative_path:
            self.project.validate_relative_path(relative_path, require_not_ignored=True)
        abs_path = os.path.join(self.get_project_root(), relative_path)
        if not os.path.exists(abs_path):
            raise FileNotFoundError(f"Relative path {relative_path} does not exist.")
        if os.path.isfile(abs_path):
            rel_paths = [relative_path]
        else:
            _dirs, rel_paths = scan_directory(
                path=abs_path,
                recursive=True,
                is_ignored_dir=self.project.is_ignored_path,
                is_ignored_file=self.project.is_ignored_path,
                relative_to=self.get_project_root(),
            )
        include_glob_matcher = GlobMatcher(paths_include_glob.strip()) if paths_include_glob.strip() else None
        exclude_glob_matcher = GlobMatcher(paths_exclude_glob.strip()) if paths_exclude_glob.strip() else None
        files: list[tuple[str, str]] = []
        for path in sorted(rel_paths):
            if include_glob_matcher and not include_glob_matcher.matches(path):
                continue
            if exclude_glob_matcher and exclude_glob_matcher.matches(path):
                continue

View on GitHub (pinned to 7fcbca7e62)

Solutions

  1. Correct the relative path (relative to the project root, forward slashes) and retry
  2. Verify existence with os.path.exists(os.path.join(project_root, relative_path)) before the call
  3. List available files (e.g. via search_for_pattern or directory listing) to find the right path
  4. If the file should exist, restore it (git checkout) — it may have been deleted by a previous edit

Example fix

// before
agent.replace_content(needle="foo", repl="bar", path="src/ap.py")
// after
rel = "src/app.py"
assert os.path.exists(os.path.join(project_root, rel)), rel
agent.replace_content(needle="foo", repl="bar", path=rel)
Defensive patterns

Strategy: validation

Validate before calling

import os
abs_path = os.path.join(project_root, relative_path)
if not os.path.exists(abs_path):
    raise FileNotFoundError(abs_path)
agent.replace_content(needle, repl, path=relative_path)

Type guard

def path_exists(rel: str, root: str) -> bool:
    return os.path.exists(os.path.join(root, rel.strip()))

Try / catch

try:
    agent.replace_content(needle, repl, path=rel)
except FileNotFoundError as e:
    if "does not exist" in str(e):
        rel = find_correct_path(rel)  # list/search project files
        agent.replace_content(needle, repl, path=rel)
    else:
        raise

Prevention

When it happens

Trigger: Passing path="missing/dir" or a typo'd file name to replace_content; passing an empty-string path that normalizes to project root but with a stripped/wrong value; referencing a path that is gitignored or outside the validated project root (caught earlier by validate_relative_path).

Common situations: Path separators mismatch (Windows-style backslashes on Linux); the file was moved/renamed by a prior automated edit; passing an absolute path where only project-relative paths are accepted.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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