modelcontextprotocol/servers · error · ValueError

Invalid path: '{f}'

Error message

Invalid path: '{f}'

What it means

In git_add(), each file path is resolved via (repo_root / f).resolve(); an OSError or RuntimeError (broken symlink, symlink loop, path with null bytes or illegal characters, permission error during resolution) is caught and rethrown as ValueError 'Invalid path: ...'. This is the resolve step, before the inside-repo check.

Source

Thrown at src/git/src/mcp_server_git/server.py:144

    return repo.git.diff(f"--unified={context_lines}", target)

def git_commit(repo: git.Repo, message: str) -> str:
    commit = repo.index.commit(message)
    return f"Changes committed successfully with hash {commit.hexsha}"

def git_add(repo: git.Repo, files: list[str]) -> str:
    if files == ["."]:
        repo.git.add(".")
    else:
        # Defense in depth: validate each path resolves within the repository
        # working tree to prevent path traversal (e.g. '../../etc/passwd' or an
        # absolute path) from staging files outside repository boundaries.
        repo_root = Path(repo.working_dir).resolve()
        for f in files:
            try:
                resolved = (repo_root / f).resolve()
            except (OSError, RuntimeError):
                raise ValueError(f"Invalid path: '{f}'")
            try:
                resolved.relative_to(repo_root)
            except ValueError:
                raise ValueError(
                    f"Path '{f}' is outside the repository '{repo_root}'"
                )
        # Use '--' to prevent files starting with '-' from being interpreted as options
        repo.git.add("--", *files)
    return "Files staged successfully"

def git_reset(repo: git.Repo) -> str:
    repo.index.reset()
    return "All staged changes reset"

def git_log(repo: git.Repo, max_count: int = 10, start_timestamp: Optional[str] = None, end_timestamp: Optional[str] = None) -> list[str]:
    if start_timestamp or end_timestamp:
        # Defense in depth: reject timestamps starting with '-' to prevent flag injection
        if start_timestamp and start_timestamp.startswith("-"):

View on GitHub (pinned to 76d64c822f)

Solutions

  1. Verify the path exists and is not a broken symlink before staging.
  2. Remove or repair the offending symlink.
  3. Sanitize paths of null bytes and illegal characters before calling.

Example fix

# before
git_add(repo, files=['link-to-nowhere'])  # dangling symlink -> ValueError

# after
def clean_add(repo, files):
    root = Path(repo.working_dir).resolve()
    ok = [f for f in files if (root / f).exists()]
    return git_add(repo, ok)
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
def resolvable(repo, f: str) -> bool:
    try:
        (Path(repo.working_dir).resolve() / f).resolve()
        return True
    except (OSError, RuntimeError):
        return False

Try / catch

try:
    git_add(repo, files)
except ValueError as e:
    if e.args[0].startswith('Invalid path:'):
        # drop/repair the offending path and retry
    raise

Prevention

When it happens

Trigger: A staged path is a dangling symlink; a symlink forms a loop; the path contains illegal characters or null bytes; resolve() hits a permissions error.

Common situations: Broken symlinks left in the working tree; OS-level path quirks; paths from untrusted input containing control characters.

Related errors


AI-assisted analysis of modelcontextprotocol/servers@76d64c822f (2026-08-12). Data as JSON: /api/errors/615b0cf047aaffe0. Report an issue: GitHub.