paperclipai/paperclip · error · 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.

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@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/3d2c841712b88063. Report an issue: GitHub.

Appendix: source

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

            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) &&
      call.tool !== READ_CURRENT_WAKE_COMMENTS_TOOL_NAME &&
      call.tool !== LIST_CHAT_ATTACHMENTS_TOOL_NAME &&
      call.tool !== REUSE_CHAT_ATTACHMENT_TOOL_NAME &&
      call.tool !== READ_CHAT_ATTACHMENT_TOOL_NAME
    ) {
      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 input = record(call.arguments);
    if (call.tool === READ_CHAT_ATTACHMENT_TOOL_NAME) {
      const scope = this.binding.chatAttachmentReadScope;
      const identityKeys = ["companyId", "issueId", "runId", "agentId"] as const;
      if (!scope || identityKeys.some((key) => scope.options.binding[key] !== this.binding[key])) {
        throw new Error("paperclip_runner_chat_attachment_read_scope_unavailable");
      }

View on GitHub (pinned to 3f1d897a7c)