BerriAI/litellm · error · HTTPException
GitHub source must include 'repo' field (e.g., 'org/repo')
Error message
GitHub source must include 'repo' field (e.g., 'org/repo')
What it means
400 thrown by _validate_plugin_source() when registering or updating a plugin whose source object has "source": "github" but no "repo" key. LiteLLM stores plugins as git references, and for the github source type the 'repo' field (in 'org/repo' form) is the only required locator.
Source
Thrown at litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py:171
raise HTTPException(
status_code=500,
detail={"error": f"Failed to generate marketplace: {e}"},
)
# Allowlist for git-subdir paths: one or more segments separated by '/'.
# Each segment must start with an alphanumeric character and contain only
# alphanumeric characters, dots, hyphens, and underscores.
# This implicitly blocks '..', leading '/', backslashes, and percent-encoded sequences.
_VALID_GIT_SUBDIR_PATH_RE: Final = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9._-]*(/[a-zA-Z0-9][a-zA-Z0-9._-]*)*$")
def _validate_plugin_source(source: Mapping[str, str]) -> None:
"""Validate plugin source format, raising HTTPException on invalid input."""
source_type: Final = source.get("source")
if source_type == "github":
if "repo" not in source:
raise HTTPException(
status_code=400,
detail={"error": "GitHub source must include 'repo' field (e.g., 'org/repo')"},
)
elif source_type == "url":
if "url" not in source:
raise HTTPException(
status_code=400,
detail={"error": "URL source must include 'url' field (e.g., 'https://github.com/org/repo.git')"},
)
elif source_type == "git-subdir":
if not source.get("url"):
raise HTTPException(
status_code=400,
detail={
"error": "git-subdir source must include 'url' field (e.g., 'https://github.com/org/repo.git')"
},
)
if not source.get("path"):View on GitHub (pinned to 77b7c6c40c)
Solutions
- Add the repo field in org/repo form to the source object: "source": {"source": "github", "repo": "org/my-plugin"}.
- Check for misspellings of the key — it must be exactly 'repo', not 'repository' or 'git_repo'.
- If you meant to point at a full git URL instead, switch the source type to "url" with a "url" field rather than github without repo.
Example fix
# before
curl -X POST http://localhost:4000/claude-code/plugins \
-d '{"name": "my-plugin", "source": {"source": "github"}}'
# after
curl -X POST http://localhost:4000/claude-code/plugins \
-d '{"name": "my-plugin", "source": {"source": "github", "repo": "org/my-plugin"}}' Defensive patterns
Strategy: validation
Validate before calling
def valid_github_source(source: dict) -> bool:
return (
source.get("source") == "github"
and isinstance(source.get("repo"), str)
and "/" in source["repo"]
)
assert valid_github_source(payload["source"]), "github source needs 'repo' ('org/repo')" Type guard
type GitHubSource = { source: 'github'; repo: string };
function isGitHubSource(s: unknown): s is GitHubSource {
return (
!!s && typeof s === 'object' &&
(s as any).source === 'github' &&
typeof (s as any).repo === 'string' && (s as any).repo.includes('/')
);
} Try / catch
try:
resp = client.post(f"{base}/claude-code/plugins", json=payload)
resp.raise_for_status()
except httpx.HTTPStatusError as e:
if e.response.status_code == 400 and "'repo' field" in e.response.text:
raise ValueError("add source.repo = 'org/repo' to the github source") from e
raise Prevention
- Build plugin registration payloads from a typed model/Pydantic schema that requires repo for github sources.
- Keep one canonical JSON example per source type in the repo and lint payloads against it in CI.
When it happens
Trigger: POST /claude-code/plugins (or PUT /claude-code/plugins/{name}) with a body like {"name": "my-plugin", "source": {"source": "github"}} — the github source object present but the repo key omitted or misspelled (e.g. "repository", "git_repo").
Common situations: Hand-writing the registration curl/JSON and forgetting the repo; adapting an example that used a "url" source and keeping only the source type key; integration code that builds the source dict conditionally and skips repo when it is empty.
Related errors
- URL source must include 'url' field (e.g., 'https://github.c
- git-subdir source must include 'url' field (e.g., 'https://g
- git-subdir source must include 'path' field (e.g., 'plugins/
- source.source must be 'github', 'url', or 'git-subdir'
- Plugin name must be kebab-case (lowercase letters, numbers,
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/aaf9f5780443149d.
Report an issue: GitHub.