Skip to content

[Bug]: checkpoint_manager subprocess.run cwd can point to non-existent directory, causing FileNotFoundError #6607

@Deyniellll

Description

@Deyniellll

Bug Description

In tools/checkpoint_manager.py, the _run_git() helper passes cwd=str(Path(working_dir).resolve()) to subprocess.run(). On Linux/macOS, Path.resolve() succeeds even when the directory does not exist — it just resolves symlinks. This means cwd can point to a non-existent path, causing subprocess.run to raise FileNotFoundError (errno 2: No such file or directory).

The FileNotFoundError is silently caught and logged, but it means checkpoints are silently disabled on any directory that happens to not exist at the time ensure_checkpoint is called.

Steps to Reproduce

  1. Use Hermes in an environment where os.getcwd() returns a path that does not exist on disk (e.g., WSL with a deleted Windows directory, Docker volume, or cross-environment paths like /mnt/c/Users/... on a Linux host).
  2. Attempt any file-mutating tool call that would trigger ensure_checkpoint.
  3. Observe FileNotFoundError in logs:
FileNotFoundError: [Errno 2] No such file or directory: /mnt/c/Users/angel/~/.hermes/job-scraper

(from actual error in errors.log on 2026-04-09 03:30:45)

Relevant Code

_run_git() at line ~107 of tools/checkpoint_manager.py:

result = subprocess.run(
    cmd,
    capture_output=True,
    text=True,
    timeout=timeout,
    env=env,
    cwd=str(Path(working_dir).resolve()),  # ← resolves but directory may not exist
)

The _git_available lazy probe at line ~223 only checks shutil.which("git"), but does not validate that working_dir exists. The error occurs inside the try block in _run_git, where it is caught as a generic FileNotFoundError.

Expected Behavior

_run_git should validate that working_dir exists before passing it as cwd to subprocess.run, and gracefully return (False, "", "working directory not found") instead of raising FileNotFoundError.

Suggestion

Add an existence check in _run_git before calling subprocess.run:

if not Path(working_dir).resolve().exists():
    return False, "", f"working directory not found: {working_dir}"

Or use _GIT_TIMEOUT and let the subprocess fail gracefully with a more descriptive error.

Additional Context

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/toolsTool registry, model_tools, toolsetssweeper:implemented-on-mainSweeper: behavior already present on current maintype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions