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

  1. Re-issue the agent API key through the proper agent enrollment flow so it is bound to a live company and agent
  2. Verify the agent and its company still exist and the agent_api_keys row references them
  3. 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

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


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