{"record":{"id":"941ccf7e190a06b9","repo":"BerriAI/litellm","slug":"str-e-941ccf","errorCode":null,"errorMessage":"str(e)","messagePattern":"str\\(e\\)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py","lineNumber":397,"sourceCode":"                    enabled=p.enabled,\n                    created_at=p.created_at.isoformat() if p.created_at else None,\n                    updated_at=p.updated_at.isoformat() if p.updated_at else None,\n                )\n            )\n\n        # Sort by created_at descending (newest first)\n        plugin_list.sort(key=lambda x: x.created_at or \"\", reverse=True)\n\n        return ListPluginsResponse(\n            plugins=plugin_list,\n            count=len(plugin_list),\n        )\n\n    except HTTPException:\n        raise\n    except Exception as e:\n        verbose_proxy_logger.exception(\"Error listing plugins: %s\", e)\n        raise HTTPException(\n            status_code=500,\n            detail={\"error\": str(e)},\n        )\n\n\n@router.get(\n    \"/claude-code/plugins/{plugin_name}\",\n    tags=[\"Claude Code Marketplace\"],\n    dependencies=[Depends(user_api_key_auth)],\n)\nasync def get_plugin(\n    plugin_name: str,\n    user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),\n):\n    \"\"\"\n    Get details of a specific plugin.\n\n    Parameters:","sourceCodeStart":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py#L379-L415","documentation":"Catch-all 500 from list_plugins (GET /claude-code/plugins). Any non-HTTPException during the Prisma find_many over the plugin table, manifest parsing, or the sort by created_at is logged as 'Error listing plugins' and surfaced as a bare str(e) 500 — the least informative of the module's error messages, so the log line is essential.","triggerScenarios":"GET /claude-code/plugins when litellm_claudecodeplugintable is missing (un-migrated schema), when the DB connection has died, or when a row's manifest_json is invalid JSON that json.loads chokes on while building PluginListItem.","commonSituations":"Immediately after a LiteLLM version upgrade that added the marketplace feature, hitting the admin list endpoint before migrations ran; polling scripts that discover the DB was restarted; a plugin row inserted by hand or by a broken earlier registration.","solutions":["Check the proxy logs for 'Error listing plugins' to get the actual traceback.","For a missing-table/relation error, restart the proxy with DATABASE_URL configured so Prisma migrations run and create the plugin table.","For a JSONDecodeError, inspect manifest_json values in the plugin table and fix or delete the offending rows, then re-register those plugins.","For connection errors, restore DB connectivity and retry — this GET is read-only and safe to retry."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    resp = client.get(f\"{base}/claude-code/plugins\", headers=hdr)\n    if resp.status_code == 200:\n        return resp.json()[\"plugins\"]\n    if resp.status_code == 500 and attempt < 2:\n        time.sleep(2 ** attempt)  # transient DB issue; read-only so retry is safe\n        continue\n    resp.raise_for_status()","preventionTips":["Run migrations after every LiteLLM upgrade before serving traffic.","Treat a 500 on this read-only list endpoint as transient first (retry), then investigate logs.","Alert on 'Error listing plugins' log lines to catch corrupted rows early."],"tags":["litellm-proxy","claude-code-marketplace","internal-server-error","database","debugging"],"backgroundTag":"internal-server-error","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}