BerriAI/litellm · error · HTTPException
source.source must be 'github', 'url', or 'git-subdir'
Error message
source.source must be 'github', 'url', or 'git-subdir'
What it means
400 thrown by _validate_plugin_source() when the source object's "source" discriminator is anything other than the three supported literals: 'github', 'url', or 'git-subdir'. The match must be exact — the check is a string equality chain, so near-misses fall through to this else-branch.
Source
Thrown at litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py:202
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"):
raise HTTPException(
status_code=400,
detail={"error": "git-subdir source must include 'path' field (e.g., 'plugins/plugin-name')"},
)
if not _VALID_GIT_SUBDIR_PATH_RE.match(source["path"]):
raise HTTPException(
status_code=400,
detail={
"error": "git-subdir 'path' must be a relative path of the form 'segment/segment' (alphanumeric, dots, hyphens, underscores only)"
},
)
else:
raise HTTPException(
status_code=400,
detail={"error": "source.source must be 'github', 'url', or 'git-subdir'"},
)
def _build_plugin_manifest(name: str, spec: PluginSpec) -> Mapping[str, object]:
"""Build the stored manifest dict shared by plugin create and update."""
dumped: Final[Mapping[str, object]] = spec.model_dump(exclude_none=True)
return {"name": name, **{key: value for key, value in dumped.items() if value and key != "name"}}
def _error_response(status_code: int, message: str) -> HTTPException:
return HTTPException(status_code=status_code, detail={"error": message})
def _name_conflict_error(name: str) -> HTTPException:
return _error_response(
409, f"A skill named '{name}' already exists. Update the existing skill instead of adding it again."View on GitHub (pinned to 77b7c6c40c)
Solutions
- Set "source" to exactly one of: "github" (with "repo"), "url" (with "url"), or "git-subdir" (with "url" and "path").
- Check for underscores, capitals, or truncation in the type string — 'git-subdir' is the only hyphenated form accepted.
- Confirm the 'source' key exists at all inside the source object; omitting it produces this same error.
Example fix
# before
{"source": {"source": "git_subdir", "url": "https://github.com/org/repo.git", "path": "plugins/p"}}
# after
{"source": {"source": "git-subdir", "url": "https://github.com/org/repo.git", "path": "plugins/p"}} Defensive patterns
Strategy: validation
Validate before calling
ALLOWED = {"github", "url", "git-subdir"}
stype = payload["source"].get("source")
if stype not in ALLOWED:
raise ValueError(f"source.source must be one of {sorted(ALLOWED)}, got {stype!r}") Type guard
const SOURCE_TYPES = ['github', 'url', 'git-subdir'] as const;
type SourceType = typeof SOURCE_TYPES[number];
function isSourceType(v: unknown): v is SourceType {
return typeof v === 'string' && (SOURCE_TYPES as readonly string[]).includes(v);
} Prevention
- Encode the three accepted literals as an enum/const in client code so typos cannot compile.
- Watch for underscore variants ('git_subdir') and capitalization when porting examples.
When it happens
Trigger: POST /claude-code/plugins or PUT with "source": {"source": "git"}, "git_subdir" (underscore), "Github" (capitalized), "gitrepo", or a null/missing source key — all hit the else branch and return this 400.
Common situations: Typo or underscore variant of the type tag; capitalization differences from copy-pasting docs headings; a missing 'source' key entirely (source.get('source') returns None); schema drift between what your tooling emits and the three accepted literals.
Related errors
- GitHub source must include 'repo' field (e.g., 'org/repo')
- 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/
- Plugin name must be kebab-case (lowercase letters, numbers,
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/604c35d949d01af0.
Report an issue: GitHub.