paperclipai/paperclip · error
Plugin is not ready (current status: ${plugin.status})
Error message
Plugin is not ready (current status: ${plugin.status}) What it means
Returned as HTTP 503 by the plugin scoped API gateway (router.use("/plugins/:pluginId/api", ...)) when the plugin record exists in the database but its lifecycle status is not "ready". Only ready plugins have a live worker, so the gateway refuses to forward the request. The offending status is embedded in the message (e.g. "disabled", "error", "upgrade_pending", "installing", "uninstalled").
Source
Thrown at server/src/routes/plugins.ts:1846
req.on("close", safeUnsubscribe);
res.on("error", safeUnsubscribe);
});
router.use("/plugins/:pluginId/api", async (req, res) => {
if (!bridgeDeps) {
res.status(501).json({ error: "Plugin scoped API routes are not enabled" });
return;
}
const { pluginId } = req.params;
const plugin = await resolvePlugin(registry, pluginId);
if (!plugin) {
res.status(404).json({ error: "Plugin not found" });
return;
}
if (plugin.status !== "ready") {
res.status(503).json({ error: `Plugin is not ready (current status: ${plugin.status})` });
return;
}
const isWorkerRunning = typeof bridgeDeps.workerManager.isRunning === "function"
? bridgeDeps.workerManager.isRunning(plugin.id)
: true;
if (!isWorkerRunning) {
res.status(503).json({ error: "Plugin worker is not running" });
return;
}
if (!plugin.manifestJson.capabilities.includes("api.routes.register")) {
res.status(404).json({ error: "Plugin does not expose scoped API routes" });
return;
}
const requestPath = req.path || "/";
const routes = plugin.manifestJson.apiRoutes ?? [];
const match = routes
.map((route) => ({ route, params: matchScopedApiRoute(route, req.method, requestPath) }))View on GitHub (pinned to a7e689b3c3)
Solutions
- GET /api/plugins/:pluginId to read the current status (the status is also named in the error message itself).
- If status is disabled, error, or upgrade_pending, have an instance admin POST /api/plugins/:pluginId/enable to transition it back to ready.
- If it should already be ready, run GET /api/plugins/:pluginId/health and fix failing checks (manifest validity, config, worker).
- In client code treat the 503 as transient only when the plugin is expected to become ready; otherwise surface the status to the operator.
Example fix
// before
const res = await fetch(`/api/plugins/${pluginId}/api/issues`);
// after
const plugin = await (await fetch(`/api/plugins/${pluginId}`)).json();
if (plugin.status !== "ready") {
await fetch(`/api/plugins/${pluginId}/enable`, { method: "POST" }); // instance admin
}
const res = await fetch(`/api/plugins/${pluginId}/api/issues`); Defensive patterns
Strategy: validation
Validate before calling
async function assertPluginReady(apiBase: string, pluginId: string): Promise<void> {
const res = await fetch(`${apiBase}/api/plugins/${encodeURIComponent(pluginId)}`);
if (res.status === 404) throw new Error(`Plugin ${pluginId} not found`);
const plugin = await res.json();
if (plugin.status !== "ready") {
throw new Error(`Plugin ${pluginId} not ready (status: ${plugin.status}) — enable it first`);
}
} Type guard
interface PluginRecord { id: string; pluginKey: string; status: string }
function isReadyPlugin(v: unknown): v is PluginRecord & { status: "ready" } {
return (
typeof v === "object" && v !== null &&
"pluginKey" in v && "status" in v &&
(v as PluginRecord).status === "ready"
);
} Try / catch
try {
await callScopedApi(pluginId, path);
} catch (err) {
if (err instanceof HttpError && err.status === 503 && /not ready/i.test(err.message)) {
const status = err.message.match(/status: (\w+)/)?.[1];
// reconcile: enable from disabled/error/upgrade_pending, then retry once
} else throw err;
} Prevention
- Check plugin.status via GET /api/plugins/:id before calling scoped API routes.
- Subscribe to plugin.ui.updated live events to react to status changes instead of discovering them via 503.
- Gate plugin UI panels on status === 'ready' so users never reach a route that will 503.
When it happens
Trigger: Any call to /api/plugins/:pluginId/api/* while the plugin row has a non-ready status: after POST /plugins/:id/disable, after a worker crash marked the plugin "error", after an upgrade added new capabilities (status "upgrade_pending" awaiting operator approval), or mid-install before activation finishes.
Common situations: UI or integration hits a plugin's scoped API right after server startup before the loader activates the plugin; plugin was disabled for maintenance; a failed upgrade left it in upgrade_pending; missing plugin config caused an error state while clients keep calling its API routes.
Related errors
- Plugin worker is not running
- Plugin does not expose scoped API routes
- Plugin API route not found
- Unable to resolve company for plugin API route
- Plugin API routes accept JSON requests only
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-18).
Data as JSON: /api/errors/8e6b58fcae165c41.
Report an issue: GitHub.