paperclipai/paperclip · warning
Request body is required
Error message
Request body is required
What it means
Returned as HTTP 400 by POST /api/plugins/tools/execute (server/src/routes/plugins.ts:1007) when req.body is falsy — the request carried no parseable JSON body. With express.json() middleware this means an empty body or a missing/wrong Content-Type (anything not application/json leaves body undefined and the cast yields undefined).
Source
Thrown at server/src/routes/plugins.ts:1022
*
* Response: `ToolExecutionResult`
* Errors:
* - 400 if request validation fails
* - 404 if tool is not found
* - 501 if tool dispatcher is not configured
* - 502 if the plugin worker is unavailable or the RPC call fails
*/
router.post("/plugins/tools/execute", async (req, res) => {
assertBoardOrAgent(req);
if (!toolDeps) {
res.status(501).json({ error: "Plugin tool dispatch is not enabled" });
return;
}
const body = (req.body as PluginToolExecuteRequest | undefined);
if (!body) {
res.status(400).json({ error: "Request body is required" });
return;
}
const { tool, parameters, runContext } = body;
// Validate required fields
if (!tool || typeof tool !== "string") {
res.status(400).json({ error: '"tool" is required and must be a string' });
return;
}
if (!runContext || typeof runContext !== "object") {
res.status(400).json({ error: '"runContext" is required and must be an object' });
return;
}
if (!runContext.agentId || !runContext.runId || !runContext.companyId || !runContext.projectId) {
res.status(400).json({View on GitHub (pinned to a7e689b3c3)
Solutions
- Send a JSON body and the header: Content-Type: application/json
- In fetch, pass headers: { 'Content-Type': 'application/json' } and body: JSON.stringify(payload)
- In curl, use: curl -X POST -H 'Content-Type: application/json' -d '{"tool":...}' <url>
Example fix
# before
curl -X POST http://localhost:3100/api/plugins/tools/execute
# after
curl -X POST http://localhost:3100/api/plugins/tools/execute \
-H 'Content-Type: application/json' \
-d '{"tool":"acme.linear:list_issues","parameters":{},"runContext":{...}}' Defensive patterns
Strategy: validation
Validate before calling
await fetch("/api/plugins/tools/execute", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ tool, parameters, runContext }),
}); Type guard
const hasJsonBody = (opts: RequestInit): boolean =>
Boolean(opts.body) && (opts.headers?.["Content-Type"] ?? "").includes("application/json"); Prevention
- Always set Content-Type: application/json and a non-empty body on POSTs to this route
- Centralize request construction in one API client so headers are never forgotten
When it happens
Trigger: POST /api/plugins/tools/execute with no body at all; a JSON body sent without the Content-Type: application/json header (so the parser skips it); curl invoked without -d; fetch() called with no body option.
Common situations: Manual curl testing that omits -H 'Content-Type: application/json' or the data flag; HTTP clients whose default POST has no body; proxies stripping content-type headers; copy-pasted request snippets missing one line.
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
- Invalid status '${String(rawStatus)}'. Must be one of: ${PLU
- Invalid file path
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-18).
Data as JSON: /api/errors/0600dfa0bd3fb584.
Report an issue: GitHub.