paperclipai/paperclip · warning
Invalid status '${String(rawStatus)}'. Must be one of: ${PLU
Error message
Invalid status '${String(rawStatus)}'. Must be one of: ${PLUGIN_STATUSES.join(", ")} What it means
Returned as HTTP 400 by GET /api/plugins (server/src/routes/plugins.ts:844) when the optional ?status= query parameter is present but is not one of the PLUGIN_STATUSES values: 'installed', 'ready', 'disabled', 'error', 'upgrade_pending', 'uninstalled'. The route validates before calling registry.listByStatus; note the code's own JSDoc omits 'disabled' but the shared constant includes it, so trust the constant's list.
Source
Thrown at server/src/routes/plugins.ts:859
* GET /api/plugins
*
* List all installed plugins, optionally filtered by lifecycle status.
*
* Query params:
* - `status` (optional): Filter by lifecycle status. Must be one of the
* values in `PLUGIN_STATUSES` (`installed`, `ready`, `error`,
* `upgrade_pending`, `uninstalled`). Returns HTTP 400 if the value is
* not a recognised status string.
*
* Response: `PluginRecord[]`
*/
router.get("/plugins", async (req, res) => {
assertBoardOrgAccess(req);
const rawStatus = req.query.status;
if (rawStatus !== undefined) {
if (typeof rawStatus !== "string" || !(PLUGIN_STATUSES as readonly string[]).includes(rawStatus)) {
res.status(400).json({
error: `Invalid status '${String(rawStatus)}'. Must be one of: ${PLUGIN_STATUSES.join(", ")}`,
});
return;
}
}
const status = rawStatus as PluginStatus | undefined;
const plugins = status
? await registry.listByStatus(status)
: await registry.listInstalled();
res.json(plugins);
});
/**
* GET /api/plugins/examples
*
* Return plugin packages bundled in this repo, if present.
* These can be installed through the normal local-path install flow.
*/
router.get("/plugins/examples", async (req, res) => {View on GitHub (pinned to a7e689b3c3)
Solutions
- Use one of the exact lowercase values: installed, ready, disabled, error, upgrade_pending, uninstalled
- Import PLUGIN_STATUSES from @paperclipai/shared in client code and validate/build the query from that constant instead of hardcoding strings
- Ensure the query serializer emits a single string value (no repeated ?status= params)
Example fix
// before
const res = await fetch(`/api/plugins?status=${filter}`); // filter = 'active'
// after
import { PLUGIN_STATUSES } from "@paperclipai/shared";
const status = PLUGIN_STATUSES.includes(filter) ? filter : undefined;
const res = await fetch(`/api/plugins${status ? `?status=${status}` : ""}`); Defensive patterns
Strategy: validation
Validate before calling
import { PLUGIN_STATUSES } from "@paperclipai/shared";
const qs = status && (PLUGIN_STATUSES as readonly string[]).includes(status)
? `?status=${encodeURIComponent(status)}`
: "";
const res = await fetch(`/api/plugins${qs}`); Type guard
import { PLUGIN_STATUSES, type PluginStatus } from "@paperclipai/shared";
const isPluginStatus = (s: string): s is PluginStatus =>
(PLUGIN_STATUSES as readonly string[]).includes(s); Prevention
- Derive filter values from PLUGIN_STATUSES instead of hardcoding status strings
- Keep status values lowercase — matching is case-sensitive
- Send ?status= exactly once; repeated params produce an array and always 400
When it happens
Trigger: GET /api/plugins?status=active, ?status=READY (case-sensitive), ?status[]=ready (array from repeated params, fails the typeof string check), or any typo like 'erorr'. Each returns 400 with the accepted values listed in the message.
Common situations: Client code written against older/assumed status vocabularies ('enabled', 'active'); case mismatches; query builders that serialize arrays for singular params; stale API clients after new statuses ('disabled', 'upgrade_pending') were added to PLUGIN_STATUSES.
Related errors
- "tool" is required and must be a string
- "runContext" is required and must be an object
- "runContext" must include agentId, runId, companyId, and pro
- Request body is required
- Invalid status '${rawStatus}'. Must be one of: ${validStatus
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-18).
Data as JSON: /api/errors/fb4ee268277efdcc.
Report an issue: GitHub.