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
- Include runContext as an object with at least agentId, runId, companyId, and projectId in the execute body
- Take the four ids from the current run/agent session state instead of reconstructing them ad hoc
- 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
- Always construct runContext from the current run session; never omit it assuming the server infers scope
- Verify the payload JSON round-trips once — double-stringified contexts arrive as strings and fail
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
- "tool" is required and must be a string
- "runContext" must include agentId, runId, companyId, and pro
- Invalid status '${String(rawStatus)}'. Must be one of: ${PLU
- Request body is required
- Invalid file path
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-18).
Data as JSON: /api/errors/e9b426938a56efd4.
Report an issue: GitHub.