paperclipai/paperclip · error

paperclip_runner_tool_not_advertised

paperclip_runner_tool_not_advertised

Error message

paperclip_runner_tool_not_advertised

What it means

PaperclipRunnerToolAuthority.execute validates every requested tool call against what the runner actually advertises. At paperclip-runner-tool-authority.ts:146 it throws 'paperclip_runner_tool_not_advertised' when the requested call.tool is not a connection tool (RUNTIME_CONNECTION_TOOL_DEFINITIONS) and is also not present in IMPLEMENTED_OPERATIONS — i.e. the agent invoked a tool name that is neither a runtime connection tool nor an implemented capability operation. This keeps the runner from executing tool names that were never advertised to the model.

Source

Thrown at server/src/services/native-runtime/paperclip-runner-tool-authority.ts:146

          const idempotencyKey = `connection-intent:tools:${this.binding.runId}:${current.digest}`;
          const delivered = () => this.db.select({ id: agentWakeupRequests.id }).from(agentWakeupRequests).where(and(
            eq(agentWakeupRequests.companyId, this.binding.companyId), eq(agentWakeupRequests.idempotencyKey, idempotencyKey),
            notInArray(agentWakeupRequests.status, ["skipped", "failed", "cancelled"]),
          )).limit(1);
          if (!(await delivered()).length) try { await this.binding.enqueueWakeup(this.binding.agentId, {
            source: "assignment", triggerDetail: "system", reason: "issue_assigned",
            payload: { issueId: this.binding.issueId, mutation: "connection_tools_refreshed" },
            idempotencyKey,
            issueStateGuard: { statuses: ["in_progress", "in_review"], assigneeAgentId: this.binding.agentId },
            requestedByActorType: "agent", requestedByActorId: this.binding.agentId,
            contextSnapshot: { issueId: this.binding.issueId, taskId: this.binding.issueId, forceFreshSession: true, wakeReason: "issue_assigned", source: "connection_tools.refreshed" },
          }); } catch (error) { if (!(await delivered()).length) throw error; }
          return { ...result, instruction: "Access is already authorized. A fresh continuation with updated tools is queued. Finish independent work, then yield. Do not request authorization again." };
        }
      }
      return result;
    }
    if (!IMPLEMENTED_OPERATIONS.has(call.tool)) throw new Error("paperclip_runner_tool_not_advertised");
    if (!runnerApiToolsEnabled(this.binding.companyId, this.binding.apiToolsEnabled) && ["search_api", "call_api"].includes(call.tool)) throw new Error("paperclip_runner_tool_not_advertised");
    const context = await this.#boundContext();
    const descriptor = CAPABILITY_SEMANTIC_TOOL_CATALOG.find((candidate) => candidate.operationId === call.tool);
    if (!descriptor || !descriptor.allowedModes.includes(
      context.issue.workMode as "standard" | "planning" | "ask",
    )) {
      throw new Error("paperclip_runner_tool_mode_denied");
    }
    const input = record(call.arguments);
    switch (call.tool) {
      case "search_api": return searchRunnerApi(call.arguments);
      case "call_api": return this.#callApi(call.callId, call.arguments);
      case "get_task_context": return {
        company: { id: this.binding.companyId },
        actor: redactedActor(context.actor),
        activeTask: redactedTask(context.issue),
        run: {
          id: this.binding.runId,

View on GitHub (pinned to 01ad858492)

Solutions

  1. Check the tool name against the runner's advertised tools list (CAPABILITY_SEMANTIC_TOOL_CATALOG operationIds plus connection tools) and correct any typo or stale name.
  2. Re-pull the current tool list for the run (toolsFor/advertised set) so the model only calls implemented operations, then retry in a fresh continuation.
  3. If the operation should exist, add it to IMPLEMENTED_OPERATIONS and CAPABILITY_SEMANTIC_TOOL_CATALOG with an implemented handler.
  4. If the call comes from a stale session/prompt, force a fresh session (forceFreshSession wakeup) with the updated tool catalog.

Example fix

// before
execute({ tool: "search_api_v2", callId, arguments });
// after
execute({ tool: "search_api", callId, arguments }); // name present in IMPLEMENTED_OPERATIONS
Defensive patterns

Strategy: validation

Validate before calling

import { IMPLEMENTED_OPERATIONS, RUNTIME_CONNECTION_TOOL_DEFINITIONS } from ".../paperclip-runner-tool-authority";
function isCallableTool(tool: string): boolean {
  return IMPLEMENTED_OPERATIONS.has(tool) || RUNTIME_CONNECTION_TOOL_DEFINITIONS.some((t) => t.name === tool);
}
if (!isCallableTool(call.tool)) throw new Error(`tool ${call.tool} is not advertised; pick one from the advertised tools list`);

Type guard

function isAdvertisedTool(tool: string, advertised: readonly { name: string }[]): tool is typeof advertised[number]["name"] {
  return advertised.some((t) => t.name === tool);
}

Try / catch

try {
  return await authority.execute(call);
} catch (err) {
  if (err instanceof Error && err.message === "paperclip_runner_tool_not_advertised") {
    return { error: "tool_not_available", hint: "Re-read get_task_context/tools list and choose an advertised tool." };
  }
  throw err;
}

Prevention

When it happens

Trigger: execute({tool, callId, arguments}) is called with a tool name that is neither in RUNTIME_CONNECTION_TOOL_DEFINITIONS nor in IMPLEMENTED_OPERATIONS — e.g. a typo'd tool name, a removed/renamed operation, a model-hallucinated tool, or a stale cached tool list referencing an old operationId.

Common situations: Model hallucinates a tool name not in its advertised list; server code renamed an operation while an old prompt/session still references the previous name; a plugin or adapter passes through arbitrary tool names; version skew between UI/adapter expectations and the deployed catalog.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/3d2c841712b88063. Report an issue: GitHub.