BerriAI/litellm · error · HTTPException

git-subdir source must include 'path' field (e.g., 'plugins/

Error message

git-subdir source must include 'path' field (e.g., 'plugins/plugin-name')

What it means

400 thrown by _validate_plugin_source() for a git-subdir source that has a url but no "path" key. LiteLLM needs the subdirectory inside the repository where the plugin lives; without it the source would be equivalent to a plain url source.

Source

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

                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:
        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]:

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Add the path field with the plugin's directory relative to the repo root: "path": "plugins/plugin-name".
  2. Use exactly the key 'path'; then make sure its value passes the segment/segment regex (no leading '/', no '..' segments, no backslashes).
  3. If the plugin actually lives at the repo root, use source type "url" instead of git-subdir.

Example fix

# before
{"source": {"source": "git-subdir", "url": "https://github.com/org/monorepo.git"}}

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

Strategy: validation

Validate before calling

source = payload["source"]
if source.get("source") == "git-subdir" and not source.get("path"):
    raise ValueError("git-subdir source requires a non-empty 'path' (e.g. 'plugins/plugin-name')")

Type guard

function hasSubdirPath(s: unknown): boolean {
  const o = s as Record<string, unknown>;
  return typeof o?.path === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9._-]*(\/[a-zA-Z0-9][a-zA-Z0-9._-]*)*$/.test(o.path);
}

Prevention

When it happens

Trigger: POST /claude-code/plugins or PUT /claude-code/plugins/{name} with {"source": {"source": "git-subdir", "url": "https://github.com/org/monorepo.git"}} — path key missing. The check is truthiness-based, so "path": "" also fails.

Common situations: Registering a monorepo plugin and forgetting the directory; using a key like "dir", "subdir", or "subpath" instead of 'path'; path built from a variable that is None/empty at runtime.

Related errors


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