paperclipai/paperclip · warning

"runContext" must include agentId, runId, companyId, and pro

Error message

"runContext" must include agentId, runId, companyId, and projectId

What it means

Returned as HTTP 400 by POST /api/plugins/tools/execute (server/src/routes/plugins.ts:1025) when runContext is an object but at least one of agentId, runId, companyId, projectId is falsy (missing, empty string, null). All four identify the execution scope checked afterwards by assertCompanyAccess and validateToolRunContextScope, so partial contexts are rejected up front.

Source

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

      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) {
      try {
        const result = await toolGatewayDeps.toolGateway.executePluginTool({
          actor: {
            type: "agent",
            agentId: req.actor.agentId,

View on GitHub (pinned to a7e689b3c3)

Solutions

  1. Populate all four fields — create a run first if runId is missing, and select/derive companyId and projectId before executing
  2. Assert non-empty values client-side before sending (a quick every() over the four keys)
  3. Update older callers that predate the projectId requirement to thread project context through

Example fix

// before
const runContext = { agentId, runId, companyId };

// after
const runContext = { agentId, runId, companyId, projectId };
const required = ["agentId", "runId", "companyId", "projectId"] as const;
if (!required.every((k) => runContext[k])) {
  throw new Error(`runContext missing: ${required.filter((k) => !runContext[k]).join(", ")}`);
}
Defensive patterns

Strategy: validation

Validate before calling

const required = ["agentId", "runId", "companyId", "projectId"] as const;
const missing = required.filter((k) => !runContext[k]);
if (missing.length) {
  throw new Error(`runContext missing required fields: ${missing.join(", ")}`);
}
await api.executePluginTool({ tool, parameters, runContext });

Type guard

type CompleteRunContext = { agentId: string; runId: string; companyId: string; projectId: string };
const isCompleteRunContext = (v: unknown): v is CompleteRunContext => {
  const c = v as Record<string, unknown>;
  return ["agentId", "runId", "companyId", "projectId"].every((k) => typeof c?.[k] === "string" && c[k] !== "");
};

Prevention

When it happens

Trigger: runContext: {agentId, runId, companyId} without projectId (common after projectId was added to the contract); any of the four set to '' after trimming form input; contexts built from optional session fields that are undefined outside a run (e.g. invoking tools from a background job with no runId).

Common situations: Clients updated for three of the four ids; test harnesses reusing a stale context object; code paths executing tools outside an active run where runId was never created; company/project context not selected yet in the UI.

Related errors


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