paperclipai/paperclip · warning

"runContext" is required and must be an object

Error message

"runContext" is required and must be an object

What it means

Returned as HTTP 400 by POST /api/plugins/tools/execute (server/src/routes/plugins.ts:1020) when the body's `runContext` field is absent, null, or not of type 'object'. The run context carries the execution scope (agentId, runId, companyId, projectId) that the dispatcher uses for authorization and audit, so it is mandatory on every execute call.

Source

Thrown at server/src/routes/plugins.ts:1035

      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({
        error: '"runContext" must include agentId, runId, companyId, and projectId',
      });
      return;
    }

    assertCompanyAccess(req, runContext.companyId);
    const scopeError = await validateToolRunContextScope(runContext);
    if (scopeError) {
      res.status(403).json({ error: scopeError });
      return;
    }

    if (req.actor.type === "agent" && toolGatewayDeps) {

View on GitHub (pinned to a7e689b3c3)

Solutions

  1. Include runContext as an object with at least agentId, runId, companyId, and projectId in the execute body
  2. Take the four ids from the current run/agent session state instead of reconstructing them ad hoc
  3. Check that JSON.stringify round-trips your payload once — double-encoded objects arrive as strings and fail this check

Example fix

// before
await api.executePluginTool({ tool, parameters });

// after
await api.executePluginTool({ tool, parameters, runContext: { agentId, runId, companyId, projectId } });
Defensive patterns

Strategy: validation

Validate before calling

if (!runContext || typeof runContext !== "object" || Array.isArray(runContext)) {
  throw new Error("runContext must be an object with agentId, runId, companyId, projectId");
}
await api.executePluginTool({ tool, parameters, runContext });

Type guard

const isRunContext = (v: unknown): v is Record<string, unknown> =>
  typeof v === "object" && v !== null && !Array.isArray(v);

Prevention

When it happens

Trigger: POSTing {"tool": "x:y"} with no runContext; runContext: null (caught by !runContext); runContext passed as a string or number (typeof !== 'object'). Note an array technically passes this check but then fails the field-presence validation with the follow-up 400.

Common situations: Adapters or scripts that only send tool + parameters and assume the server infers context from auth; runContext lost through serialization bugs (stringified twice, so it arrives as a string); older clients predating the runContext requirement.

Related errors


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