chatboxai/chatbox · error · Error

Tool "${batchPart.toolName}" is not available

Error message

Tool "${batchPart.toolName}" is not available

What it means

Thrown during batched execution of paused tool calls. After the user approves a paused batch, buildToolsForPausedToolCall rebuilds the toolset and each batch part is dispatched by name; if tools[batchPart.toolName] is absent or lacks an execute function, the call cannot run and the batch is aborted. This guards against dispatching a tool the session no longer exposes.

Source

Thrown at src/renderer/stores/session/orchestration.ts:1287

        // time spent waiting for user approval / manual continuation.
        startTime: Date.now(),
        duration: undefined,
      })
    ),
    generating: true,
    cancel: (stoppedAt = Date.now()) => controller.abort(stoppedAt),
  }
  await modifyMessage(sessionId, message, false)

  try {
    const { tools } = await buildToolsForPausedToolCall(session, settings, message)
    for (const batchPart of batch) {
      if (controller.signal.aborted) break

      const toolValue = (tools as Record<string, unknown>)[batchPart.toolName]
      const executableTool = toolValue && typeof toolValue === 'object' ? (toolValue as ExecutableTool) : undefined
      if (typeof executableTool?.execute !== 'function') {
        throw new Error(`Tool "${batchPart.toolName}" is not available`)
      }

      try {
        // Bind approval to the exact call the user reviewed. Never infer authorization from
        // batch membership: a sibling call must pass through its own approval gate.
        const result = await executableTool.execute(
          batchPart.args,
          createPausedToolCallExecutionContext(batchPart, approvedToolCallId, controller.signal)
        )
        message = updateToolCallPart(message, batchPart.toolCallId, (toolPart) => ({
          ...toolPart,
          state: 'result',
          pauseReason: undefined,
          result,
          duration: toolPart.startTime ? Date.now() - toolPart.startTime : undefined,
        }))
      } catch (error) {
        if (controller.signal.aborted) {

View on GitHub (pinned to 81571269ad)

Solutions

  1. Re-validate each batchPart.toolName against the rebuilt tools map and mark only missing ones as errored instead of aborting the whole batch.
  2. Prompt the user to re-resume after settings changes rather than failing silently.
  3. Keep tool names stable across versions and deprecate rather than rename.
  4. Log the available tool keys alongside the missing name to speed diagnosis.

Example fix

// before
const toolValue = (tools as Record<string, unknown>)[batchPart.toolName]
const executableTool = toolValue && typeof toolValue === 'object' ? (toolValue as ExecutableTool) : undefined
if (typeof executableTool?.execute !== 'function') {
  throw new Error(`Tool "${batchPart.toolName}" is not available`)
}
// after
const toolValue = (tools as Record<string, unknown>)[batchPart.toolName]
const executableTool = toolValue && typeof toolValue === 'object' ? (toolValue as ExecutableTool) : undefined
if (typeof executableTool?.execute !== 'function') {
  message = updateToolCallPart(message, batchPart.toolCallId, (p) => ({ ...p, state: 'error', result: { error: `Tool "${batchPart.toolName}" is no longer available (settings changed?)` } }))
  continue
}
Defensive patterns

Strategy: validation

Validate before calling

const { tools } = await buildToolsForPausedToolCall(session, settings, message)
const missing = batch.filter((p) => typeof (tools as Record<string, unknown>)[p.toolName]?.execute !== 'function')
if (missing.length) {
  // mark only those parts as errored, continue the rest
}

Type guard

function isExecutableTool(v: unknown): v is { execute: (...args: unknown[]) => Promise<unknown> } {
  return typeof v === 'object' && v !== null && typeof (v as { execute?: unknown }).execute === 'function'
}

Try / catch

try {
  await runBatch()
} catch (err) {
  if (err instanceof Error && /is not available$/.test(err.message)) {
    markToolPartsErrored(message, batch)
  } else throw err
}

Prevention

When it happens

Trigger: buildToolsForPausedToolCall returns a tools map that does not include batchPart.toolName or includes a non-executable entry. Causes: settings changed between pause and approval (tool disabled, agent mode toggled off, knowledge base detached), the model emitted a tool name not in the allowed set, or a version change renamed/removed a tool.

Common situations: User paused on a tool call then changed settings (disabled agent mode, removed knowledge base, switched provider) before approving; a model hallucinated a tool name; a plugin/tool was unregistered in an update while a paused call waited.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/e7dc2d5fcea3837a. Report an issue: GitHub.