{"record":{"id":"748a05e3dac19f30","repo":"vercel/ai","slug":"invalid-host-tool-catalog","errorCode":null,"errorMessage":"Invalid host tool catalog.","messagePattern":"Invalid host tool catalog\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness-acp/src/v1/bridge/host-tool-mcp.ts","lineNumber":116,"sourceCode":"    relayUrl,\n    relayCredential,\n    path,\n    body,\n  });\n  const { value } = response;\n  if (!response.ok) {\n    throw new Error(readErrorMessage({ value, status: response.status }));\n  }\n  return value;\n}\n\nfunction validateToolCatalog({\n  value,\n}: {\n  value: unknown;\n}): ReadonlyArray<HarnessV1BridgeToolWire> {\n  if (!Array.isArray(value) || !value.every(isTool)) {\n    throw new Error('Invalid host tool catalog.');\n  }\n  return value;\n}\n\nasync function readToolCatalog({\n  path,\n}: {\n  path: string;\n}): Promise<ReadonlyArray<HarnessV1BridgeToolWire>> {\n  const text = await readFile(path, 'utf8');\n  const value = await new Response(text, {\n    headers: { 'content-type': 'application/json' },\n  }).json();\n  return validateToolCatalog({ value });\n}\n\nfunction isTool(value: unknown): value is HarnessV1BridgeToolWire {\n  return (","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-acp/src/v1/bridge/host-tool-mcp.ts#L98-L134","documentation":"Thrown by validateToolCatalog when the tool catalog data is not an array of valid tool entries. Each entry must be an object with a string 'name', optional string 'description', and optional object (non-array) 'inputSchema'. This runs both on the catalog file read at startup (readToolCatalog) and on every catalog update received from the relay during watchCatalog.","triggerScenarios":"(1) The file at AI_SDK_ACP_HOST_TOOLS_FILE contains JSON that is not an array, or entries missing 'name', with a non-string name/description, or an array 'inputSchema'. (2) The relay's /catalog/next update carries a 'tools' field failing the same checks.","commonSituations":"Hand-edited tools file with a typo (e.g. 'inputSchema': [] or name misspelled as 'toolName'); JSON written as a single object instead of an array; a newer/older wire format where the schema field was renamed; relay host writing malformed catalog JSON.","solutions":["Open AI_SDK_ACP_HOST_TOOLS_FILE and validate it is a JSON array of objects each with a string 'name'.","Fix offending entries: rename 'toolName'->'name', remove or convert array 'inputSchema' to a JSON Schema object, or delete the invalid entry.","Validate the file against HarnessV1BridgeToolWire (name: string, description?: string, inputSchema?: object).","If the error comes from a relay update, inspect the host's tool registration code to see why it emits an invalid tool entry."],"exampleFix":"// before (tools file)\n[{ \"toolName\": \"read_file\", \"inputSchema\": [] }]\n// after\n[{ \"name\": \"read_file\", \"description\": \"Read a file\", \"inputSchema\": { \"type\": \"object\", \"properties\": {} } }]","handlingStrategy":"validation","validationCode":"function isValidToolEntry(t) {\n  return (\n    t != null && typeof t === 'object' && !Array.isArray(t) &&\n    typeof t.name === 'string' &&\n    (t.description === undefined || typeof t.description === 'string') &&\n    (t.inputSchema === undefined || (typeof t.inputSchema === 'object' && t.inputSchema !== null && !Array.isArray(t.inputSchema)))\n  );\n}\nconst tools = JSON.parse(fs.readFileSync(process.env.AI_SDK_ACP_HOST_TOOLS_FILE, 'utf8'));\nif (!Array.isArray(tools) || !tools.every(isValidToolEntry)) throw new Error('tools file fails HarnessV1BridgeToolWire shape');","typeGuard":"function isHarnessToolWire(value: unknown): value is HarnessV1BridgeToolWire {\n  return (\n    value != null && typeof value === 'object' && !Array.isArray(value) &&\n    typeof (value as any).name === 'string' &&\n    ((value as any).description === undefined || typeof (value as any).description === 'string') &&\n    ((value as any).inputSchema === undefined || (typeof (value as any).inputSchema === 'object' && (value as any).inputSchema !== null && !Array.isArray((value as any).inputSchema)))\n  );\n}","tryCatchPattern":"let tools;\ntry {\n  tools = await readToolCatalog({ path: catalogPath });\n} catch (error) {\n  throw new Error(`Host tools file ${catalogPath} is invalid: ${error instanceof Error ? error.message : String(error)}`);\n}","preventionTips":["Validate the tools JSON file in CI against HarnessV1BridgeToolWire.","Generate the tools file programmatically instead of hand-editing it.","Never use arrays for inputSchema - it must be a JSON Schema object.","Lint tool entries for the exact field names ('name', not 'toolName')."],"tags":["validation","schema","config","json"],"backgroundTag":"schema-validation-failed","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}