{"record":{"id":"63c0fd3406144d23","repo":"vercel/ai","slug":"host-tool-body-toolname-is-not-active-in-catalo","errorCode":null,"errorMessage":"Host tool ${body.toolName} is not active in catalog revision ${state.revision}.","messagePattern":"Host tool (.+?) is not active in catalog revision (.+?)\\.","errorType":"http","errorClass":"RelayRequestError","httpStatus":404,"severity":"error","filePath":"packages/harness-acp/src/v1/bridge/host-tool-relay.ts","lineNumber":299,"sourceCode":"    !isRecord(body.input) ||\n    !Number.isSafeInteger(body.catalogRevision)\n  ) {\n    throw new RelayRequestError({\n      status: 400,\n      message: 'Invalid host tool relay request.',\n    });\n  }\n  if (body.catalogRevision !== state.revision) {\n    throw new RelayRequestError({\n      status: 409,\n      message:\n        `Host tool ${body.toolName} was invoked from stale catalog revision ` +\n        `${body.catalogRevision}; the active revision is ${state.revision}.`,\n    });\n  }\n  const tool = state.tools.find(item => item.name === body.toolName);\n  if (tool == null) {\n    throw new RelayRequestError({\n      status: 404,\n      message:\n        `Host tool ${body.toolName} is not active in catalog revision ` +\n        `${state.revision}.`,\n    });\n  }\n\n  const correlationToken = randomBytes(32).toString('hex');\n  turn.registerCorrelationInvocation({\n    token: correlationToken,\n    serverName,\n    toolName: tool.name,\n    input: body.input,\n    order: nextInvocationOrder(),\n  });\n  turn.emitToolCall({\n    toolCallId: body.requestId,\n    toolName: tool.name,","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-acp/src/v1/bridge/host-tool-relay.ts#L281-L317","documentation":"Even with a matching catalogRevision, the requested tool must exist in the relay's current tool list (state.tools, matched by exact name). If no tool with that name is registered, handleInvocation returns this 404 RelayRequestError. This guards against invoking tools the host never exposed or that were removed without a client-side revision mismatch.","triggerScenarios":"Invoking a toolName that is not in the host's registered tool set — typos, case mismatches, calling a tool removed by the most recent updateCatalog, or a client fabricating tool names not advertised in the catalog.","commonSituations":"Model hallucinating a tool name the host never registered; prompt/driver renaming tools between catalog fetch and invoke; tool removed in a newer catalog while revision coincidentally matched (e.g. client not tracking revision properly); custom adapters aliasing names differently.","solutions":["Only invoke tool names exactly as listed in the current /catalog/next response (match case and spelling).","Re-fetch the catalog after any updateCatalog call and rebuild the client's tool list before invoking.","Handle 404 by listing available tools back to the model/agent so it can pick a valid tool.","If the tool should exist, verify the host actually registered it in the tools array passed to startHostToolRelay / updateCatalog."],"exampleFix":"// before\nawait invokeHostTool('file_search', input); // guessed name\n// after\nconst catalog = await pollCatalog(revision);\nif (!catalog.tools.some(t => t.name === 'file_search')) {\n  throw new Error('file_search not offered by host; available: ' + catalog.tools.map(t => t.name).join(', '));\n}\nawait invokeHostTool('file_search', input);","handlingStrategy":"type-guard","validationCode":"function assertToolAvailable(tools: ReadonlyArray<{ name: string }>, toolName: string) {\n  if (!tools.some(t => t.name === toolName)) {\n    throw new Error(`Tool '${toolName}' is not registered. Available: ${tools.map(t => t.name).join(', ')}`);\n  }\n}","typeGuard":"function isToolInCatalog(tools: ReadonlyArray<{ readonly name: string }>, toolName: string): boolean {\n  return tools.some(item => item.name === toolName);\n}","tryCatchPattern":"const res = await postJson(invokeUrl, body);\nif (res.status === 404 && /is not active in catalog revision/.test((await res.json()).error ?? '')) {\n  // report available tools back to the agent/model so it can choose a valid one\n}","preventionTips":["Only invoke names taken verbatim from the current catalog response.","Rebuild the client tool list after each revision change.","Validate tool names against the catalog before dispatching from model output.","Confirm the host registers the tool in the tools array passed to startHostToolRelay/updateCatalog."],"tags":["relay","tool-not-found","catalog","harness-acp"],"backgroundTag":"tool-not-found","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}