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

  1. Send a JSON body and the header: Content-Type: application/json
  2. In fetch, pass headers: { 'Content-Type': 'application/json' } and body: JSON.stringify(payload)
  3. 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

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


AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-18). Data as JSON: /api/errors/0600dfa0bd3fb584. Report an issue: GitHub.