modelcontextprotocol/servers · error · BadName
Invalid target: '{target}' - cannot start with '-'
Error message
Invalid target: '{target}' - cannot start with '-' What it means
git_diff() rejects a target that starts with '-' as defense-in-depth against flag injection into `git diff` (e.g. a target like --upload-pack=...). The check runs before repo.rev_parse(target), so it blocks the value regardless of whether such a ref exists. Raises gitdb BadName, which propagates raw to the client because the git server runs with raise_exceptions=True and call_tool does not wrap it.
Source
Thrown at src/git/src/mcp_server_git/server.py:124
CHECKOUT = "git_checkout"
SHOW = "git_show"
BRANCH = "git_branch"
def git_status(repo: git.Repo) -> str:
return repo.git.status()
def git_diff_unstaged(repo: git.Repo, context_lines: int = DEFAULT_CONTEXT_LINES) -> str:
return repo.git.diff(f"--unified={context_lines}")
def git_diff_staged(repo: git.Repo, context_lines: int = DEFAULT_CONTEXT_LINES) -> str:
return repo.git.diff(f"--unified={context_lines}", "--cached")
def git_diff(repo: git.Repo, target: str, context_lines: int = DEFAULT_CONTEXT_LINES) -> str:
# Defense in depth: reject targets starting with '-' to prevent flag injection,
# even if a malicious ref with that name exists (e.g. via filesystem manipulation)
if target.startswith("-"):
raise BadName(f"Invalid target: '{target}' - cannot start with '-'")
repo.rev_parse(target) # Validates target is a real git ref, throws BadName if not
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()View on GitHub (pinned to 76d64c822f)
Solutions
- Reject or strip a leading '-' from target before calling.
- Use a valid ref (branch, tag, or commit SHA) that does not start with '-'.
- If the value legitimately could be an option, pass it through the proper option parameter instead of target.
Example fix
# before
git_diff(repo, target='-eMalicious') # -> BadName
# after
def safe_target(t: str) -> str:
if t.startswith('-'):
raise ValueError('target must not start with -')
return t
git_diff(repo, safe_target(target)) Defensive patterns
Strategy: validation
Validate before calling
def safe_ref(target: str) -> str:
if not target or target.startswith('-'):
raise ValueError('target must be a non-empty ref not starting with -')
return target Try / catch
from gitdb.exc import BadName
try:
git_diff(repo, target)
except BadName as e:
if 'cannot start with' in str(e):
# reject/normalize input
raise Prevention
- Reject any ref/option value beginning with '-' before passing to git helpers.
- Prefer SHAs/tags over free-form strings from untrusted callers.
- Keep the call_tool layer sanitizing user-supplied refs.
When it happens
Trigger: Calling git_diff with a target string beginning with '-'; malicious or malformed ref input.
Common situations: Adversarial tool input; copy-paste artifacts; a UI that lets users pass arbitrary ref text.
Related errors
- Invalid start_timestamp: '{start_timestamp}' - cannot start
- Invalid end_timestamp: '{end_timestamp}' - cannot start with
- Invalid branch name: '{branch_name}' - cannot start with '-'
- Invalid base branch: '{base_branch}' - cannot start with '-'
- Invalid revision: '{revision}' - cannot start with '-'
AI-assisted analysis of modelcontextprotocol/servers@76d64c822f (2026-08-12).
Data as JSON: /api/errors/5f60fc1a1e3890eb.
Report an issue: GitHub.