BerriAI/litellm · error · HTTPException

git-subdir source must include 'url' field (e.g., 'https://g

Error message

git-subdir source must include 'url' field (e.g., 'https://github.com/org/repo.git')

What it means

400 thrown by _validate_plugin_source() for a git-subdir plugin source that lacks a truthy "url" value. git-subdir sources point at a subdirectory of a git repository, so LiteLLM requires both the repository url and the relative path; this error fires on the missing/falsy url check (note it uses `if not source.get("url")`, so an empty string also triggers it).

Source

Thrown at litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py:183

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"):
            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:

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Include a non-empty repository url alongside the path: "source": {"source": "git-subdir", "url": "https://github.com/org/monorepo.git", "path": "plugins/my-plugin"}.
  2. Check that the url value is not an empty string — unlike the github/url checks, this branch rejects falsy values, not just missing keys.
  3. Verify the 'path' field is also present and matches the segment/segment allowlist, otherwise you will get the follow-up path validation error.

Example fix

# before
{"source": {"source": "git-subdir", "path": "plugins/my-plugin"}}

# after
{"source": {"source": "git-subdir", "url": "https://github.com/org/monorepo.git", "path": "plugins/my-plugin"}}
Defensive patterns

Strategy: validation

Validate before calling

source = payload["source"]
if source.get("source") == "git-subdir":
    if not source.get("url"):
        raise ValueError("git-subdir source requires a non-empty 'url'")
    if not source.get("path"):
        raise ValueError("git-subdir source requires 'path'")

Type guard

type GitSubdirSource = { source: 'git-subdir'; url: string; path: string };
function isGitSubdirSource(s: unknown): s is GitSubdirSource {
  const o = s as Record<string, unknown>;
  return (
    !!o && o.source === 'git-subdir' &&
    typeof o.url === 'string' && o.url.length > 0 &&
    typeof o.path === 'string' && o.path.length > 0
  );
}

Prevention

When it happens

Trigger: POST /claude-code/plugins or PUT /claude-code/plugins/{name} with {"source": {"source": "git-subdir", "path": "plugins/my-plugin"}} — url absent — or with "url": "" (empty string), which fails the truthiness check.

Common situations: Splitting a monorepo plugin into git-subdir form and forgetting the repo URL; templating code that renders an empty url when the env/config variable is unset; assuming the path alone identifies the plugin.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/28e3b2276d3e3268. Report an issue: GitHub.