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
- 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.
- 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.
- If the operation should exist, add it to IMPLEMENTED_OPERATIONS and CAPABILITY_SEMANTIC_TOOL_CATALOG with an implemented handler.
- 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
- Always derive model-facing tool lists from the same IMPLEMENTED_OPERATIONS/catalog source the executor checks.
- Validate tool names against the advertised list before dispatching calls from adapters/plugins.
- Force a fresh session when the server's tool catalog changes so old tool names are not replayed.
- Add a fuzz/unit test that dispatches every catalog operationId through execute().
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.