{"record":{"id":"455f9c956e05db2f","repo":"vercel/ai","slug":"invalid-host-tool-relay-response","errorCode":null,"errorMessage":"Invalid host tool relay response.","messagePattern":"Invalid host tool relay response\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness-acp/src/v1/bridge/host-tool-mcp.ts","lineNumber":154,"sourceCode":"    typeof value.name === 'string' &&\n    (value.description === undefined ||\n      typeof value.description === 'string') &&\n    (value.inputSchema === undefined ||\n      (isRecord(value.inputSchema) && !Array.isArray(value.inputSchema)))\n  );\n}\n\nfunction validateInvocationResult({\n  value,\n}: {\n  value: unknown;\n}): HostToolMCPInvocationResult {\n  if (\n    !isRecord(value) ||\n    typeof value.correlationToken !== 'string' ||\n    (value.isError !== undefined && typeof value.isError !== 'boolean')\n  ) {\n    throw new Error('Invalid host tool relay response.');\n  }\n  return {\n    output: value.output,\n    ...(value.isError ? { isError: true } : {}),\n    correlationToken: value.correlationToken,\n  };\n}\n\nfunction readErrorMessage({\n  value,\n  status,\n}: {\n  value: unknown;\n  status: number;\n}): string {\n  return isRecord(value) && typeof value.error === 'string'\n    ? value.error\n    : `Host tool relay returned HTTP ${status}.`;","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-acp/src/v1/bridge/host-tool-mcp.ts#L136-L172","documentation":"Thrown by validateInvocationResult when the relay's /invoke response is not a well-formed invocation result: the body is not an object, lacks a string 'correlationToken', or has a non-boolean 'isError'. The bridge uses this to guarantee every tool invocation result handed to the MCP server has a correlation token matching the request.","triggerScenarios":"hostToolServer invoke -> postRelay('/invoke') returning HTTP 200 with a body that: is not a JSON object, omits 'correlationToken', has a non-string correlationToken, or has 'isError' set to a non-boolean value.","commonSituations":"Relay implementation bug dropping correlationToken on success paths; a middleware/proxy wrapping or replacing the response body; relay/bridge version mismatch after a wire-format change; relay echoing an error envelope with HTTP 200.","solutions":["Inspect the relay's /invoke handler and confirm it always echoes the request's correlationToken as a string in the response.","Check relay and bridge package versions match; align to the same AI SDK release.","Capture the raw response body to check for an enveloping proxy rewriting it.","If 'isError' is set, ensure it is a JSON boolean (true/false), not the strings \"true\"/\"false\"."],"exampleFix":"// before: relay response\n{ \"output\": \"done\" }\n// after\n{ \"output\": \"done\", \"correlationToken\": \"<requestId from invoke>\", \"isError\": false }","handlingStrategy":"type-guard","validationCode":"function isValidInvocationResponse(value) {\n  return (\n    value != null && typeof value === 'object' && !Array.isArray(value) &&\n    typeof value.correlationToken === 'string' &&\n    (value.isError === undefined || typeof value.isError === 'boolean')\n  );\n}\n// check before accepting the relay body as an invocation result","typeGuard":"function isInvocationResult(value: unknown): value is HostToolMCPInvocationResult {\n  return (\n    value != null && typeof value === 'object' && !Array.isArray(value) &&\n    typeof (value as any).correlationToken === 'string' &&\n    ((value as any).isError === undefined || typeof (value as any).isError === 'boolean')\n  );\n}","tryCatchPattern":"try {\n  const result = await invokeViaRelay({ toolName, input });\n} catch (error) {\n  // 'Invalid host tool relay response.' => relay violated the /invoke contract;\n  // return an MCP tool error to the client rather than crashing the server.\n  return { isError: true, message: error instanceof Error ? error.message : String(error) };\n}","preventionTips":["Add a relay contract test asserting correlationToken is echoed on every /invoke response.","Keep relay and bridge on matching package versions.","Ensure isError is serialized as a JSON boolean, never a string.","Bypass or audit proxies/middleware that might rewrap the response body."],"tags":["relay","protocol-mismatch","validation","rpc"],"backgroundTag":"invalid-api-response-shape","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}