BerriAI/litellm · error · HTTPException
Registration failed: {e}
Error message
Registration failed: {e} What it means
Catch-all 500 from register_plugin (POST /claude-code/plugins). HTTPExceptions (the 400s above, name-conflict 409 from UniqueViolationError, DB-not-connected) are re-raised untouched; everything else — unexpected DB failures, missing plugin table, serialization errors — is logged as 'Error registering plugin' and returned as this generic message with the exception string interpolated.
Source
Thrown at litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py:328
return RegisterPluginResponse(
status="success",
action="created",
plugin=PluginResponse(
id=plugin.id,
name=plugin.name,
version=plugin.version,
description=plugin.description,
source=request.source,
enabled=plugin.enabled,
),
)
except HTTPException:
raise
except Exception as e:
verbose_proxy_logger.exception("Error registering plugin: %s", e)
raise HTTPException(
status_code=500,
detail={"error": f"Registration failed: {e}"},
)
@router.get(
"/claude-code/plugins",
tags=["Claude Code Marketplace"],
dependencies=[Depends(user_api_key_auth)],
response_model=ListPluginsResponse,
)
async def list_plugins(
enabled_only: bool = False,
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
):
"""
List all plugins in the marketplace.
View on GitHub (pinned to 77b7c6c40c)
Solutions
- Read the proxy log: 'Error registering plugin: ...' carries the full traceback (verbose_proxy_logger.exception) — that names the real failure.
- If the traceback says the table/relation does not exist, restart the proxy with DATABASE_URL set so Prisma migrations create it, or run the migrations against the database manually.
- If it is a connectivity error, verify Postgres is up and credentials valid, then retry the POST once recovered (registration is not idempotent — a 409 on retry means the first attempt actually landed).
- Confirm you are not hitting a genuine duplicate-name path via a different code path (direct 409 'already exists' responses are handled separately and will not produce this 500).
Defensive patterns
Strategy: try-catch
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 == 500:
# ambiguous: the insert may or may not have committed.
existing = client.get(f"{base}/claude-code/plugins", headers=hdr)
if any(p["name"] == payload["name"] for p in existing.json()["plugins"]):
return # actually registered on the first attempt
raise
if e.response.status_code == 409:
return # name conflict - treat as registered
raise Prevention
- Validate the whole payload client-side (name + source rules) before POST to eliminate the 400 paths.
- After an ambiguous 500, verify with a follow-up GET instead of blindly re-POSTing (409 vs created).
- Keep proxy version and DB migrations in sync so the plugin table always exists.
When it happens
Trigger: POST /claude-code/plugins when the litellm_claudecodeplugintable does not exist yet (DB migrated by an older LiteLLM before this feature shipped), when the Postgres connection drops mid-insert, or when the manifest built from PluginSpec fails to serialize for the DB write.
Common situations: Upgrading LiteLLM to a version that introduced the plugin marketplace while pointing at an existing database that was never re-migrated; transient DB outage during a CI script that registers many plugins; prisma client in a bad state after a DB restart.
Related errors
- Failed to generate marketplace: {e}
- str(e)
- DB not connected. This endpoint needs a database; set DATABA
- GitHub source must include 'repo' field (e.g., 'org/repo')
- URL source must include 'url' field (e.g., 'https://github.c
AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18).
Data as JSON: /api/errors/d149c9b02119c2d7.
Report an issue: GitHub.