paperclipai/paperclip · error
Agent identity is required
Error message
Agent identity is required
What it means
Returned as HTTP 401 by GET /api/plugins/tools (server/src/routes/plugins.ts:961) when the request authenticated as an agent actor (bearer agent API key) and the tool gateway is configured, but the actor lacks companyId or agentId. The tool gateway scopes plugin tool visibility per company+agent, so an agent identity without both bindings cannot be authorized.
Source
Thrown at server/src/routes/plugins.ts:976
*
* Query params:
* - `pluginId` (optional): Filter to tools from a specific plugin
*
* Response: `AgentToolDescriptor[]`
* Errors: 501 if tool dispatcher is not configured
*/
router.get("/plugins/tools", async (req, res) => {
assertBoardOrAgent(req);
if (!toolDeps) {
res.status(501).json({ error: "Plugin tool dispatch is not enabled" });
return;
}
const pluginId = req.query.pluginId as string | undefined;
if (req.actor.type === "agent" && toolGatewayDeps) {
if (!req.actor.companyId || !req.actor.agentId) {
res.status(401).json({ error: "Agent identity is required" });
return;
}
const tools = await toolGatewayDeps.toolGateway.listPluginToolsForAgent({
companyId: req.actor.companyId,
agentId: req.actor.agentId,
});
res.json(pluginId ? tools.filter((tool) => tool.pluginId === pluginId || tool.name.startsWith(`${pluginId}:`)) : tools);
return;
}
const filter = pluginId ? { pluginId } : undefined;
const tools = toolDeps.toolDispatcher.listToolsForAgent(filter);
res.json(tools);
});
/**
* POST /api/plugins/tools/execute
*View on GitHub (pinned to a7e689b3c3)
Solutions
- Re-issue the agent API key through the proper agent enrollment flow so it is bound to a live company and agent
- Verify the agent and its company still exist and the agent_api_keys row references them
- Confirm the request uses the Authorization: Bearer <agent-key> header (not a board session) for agent-context calls
Defensive patterns
Strategy: validation
Validate before calling
// Before executing agent-scoped calls, confirm the key resolves to full identity
const me = await api.getAgentIdentity(); // returns actor context
if (!me.companyId || !me.agentId) {
throw new Error("Agent key is not bound to a company/agent — re-enroll the key");
} Type guard
type Actor = { type: string; companyId?: string; agentId?: string };
const hasAgentIdentity = (
a: Actor,
): a is Actor & { companyId: string; agentId: string } =>
a.type === "agent" && typeof a.companyId === "string" && typeof a.agentId === "string"; Prevention
- Issue agent API keys only through agent enrollment so they always carry companyId/agentId bindings
- Delete or rotate keys when their agent or company is removed
When it happens
Trigger: Calling GET /api/plugins/tools with an agent API key whose actor resolution produced no companyId/agentId — e.g. the key is not linked to an agent row, the linked agent or company was deleted while the key still validates, or a hand-crafted key context missing claims. Only affects req.actor.type === 'agent'; board actors skip this branch.
Common situations: Using an orphaned agent_api_keys entry after the agent was removed; key issued before company enrollment completed; copying keys between environments where the agent records differ.
Related errors
- Plugin tool dispatch is not enabled
- Request body is required
- "tool" is required and must be a string
- "runContext" is required and must be an object
- "runContext" must include agentId, runId, companyId, and pro
AI-assisted analysis of paperclipai/paperclip@a7e689b3c3 (2026-08-18).
Data as JSON: /api/errors/b029c3bafc08d44f.
Report an issue: GitHub.