{"record":{"id":"74106e2f966f6b0b","repo":"vercel/ai","slug":"tool-relay-schema-name-failed-with-res-status","errorCode":null,"errorMessage":"Tool relay ${schema.name} failed with ${res.status}: ${body.slice(0, 500)}","messagePattern":"Tool relay (.+?) failed with (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness-opencode/src/bridge/host-tool-mcp.ts","lineNumber":63,"sourceCode":"for (const schema of schemas) {\n  const shape = toZodShape(schema.inputSchema);\n  server.tool(\n    schema.name,\n    schema.description ?? '',\n    shape,\n    async (input: Record<string, unknown>) => {\n      const requestId = crypto.randomUUID();\n      try {\n        const res = await fetch(relayUrl, {\n          method: 'POST',\n          headers: {\n            'Content-Type': 'application/json',\n          },\n          body: JSON.stringify({ requestId, toolName: schema.name, input }),\n        });\n        if (!res.ok) {\n          const body = await res.text();\n          throw new Error(\n            `Tool relay ${schema.name} failed with ${res.status}: ${body.slice(0, 500)}`,\n          );\n        }\n        const data = (await res.json()) as { result?: unknown };\n        return {\n          content: [\n            {\n              type: 'text' as const,\n              text: JSON.stringify(data.result ?? null),\n            },\n          ],\n        };\n      } catch (err) {\n        return {\n          content: [{ type: 'text' as const, text: `Error: ${String(err)}` }],\n          isError: true,\n        };\n      }","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-opencode/src/bridge/host-tool-mcp.ts#L45-L81","documentation":"The in-sandbox MCP host-tool server relays each tool invocation to the host process over HTTP via TOOL_RELAY_URL. When the relay responds with a non-2xx status, the bridge turns it into an error containing the tool name, HTTP status, and the first 500 chars of the response body, which is then surfaced to the model as a tool error. This indicates the host-side relay endpoint rejected or failed to execute the tool call.","triggerScenarios":"Calling any bridged host tool from inside the OpenCode sandbox when the TOOL_RELAY_URL HTTP endpoint returns a non-OK status (400/404/500, etc.), e.g. the host relay process crashed, the URL points to the wrong port/path, or the host handler threw while executing the tool.","commonSituations":"Host process restarted or exited mid-session leaving the relay dead; misconfigured TOOL_RELAY_URL port; the host tool implementation itself raised an exception that the relay returned as a 500; auth/CORS proxy in front of the relay rejecting the POST.","solutions":["Check the response body in the error message for the relay's own error detail and fix the host-side tool implementation accordingly.","Verify TOOL_RELAY_URL matches the host relay's actual address and port and that the relay process is still running.","Retry the tool call; transient host restarts cause one-off 5xx responses.","Ensure the sandbox can reach the host network (no firewall/network isolation blocking the relay)."],"exampleFix":"// before (host relay returning 500 because handler throws)\nrelay.post('/tool', async (req, res) => { await runTool(req.body); res.send(...) });\n// after\nrelay.post('/tool', async (req, res) => {\n  try { const result = await runTool(req.body); res.json({ result }); }\n  catch (e) { console.error('tool failed', e); res.status(500).json({ error: String(e) }); }\n});","handlingStrategy":"try-catch","validationCode":"const relayOk = await fetch(process.env.TOOL_RELAY_URL, { method: 'HEAD' }).then(r => r.ok).catch(() => false);\nif (!relayOk) throw new Error('Tool relay unreachable before session start');","typeGuard":"function isToolRelayHttpError(e: unknown): e is Error & { message: string } {\n  return e instanceof Error && /^Tool relay .+ failed with \\d{3}:/.test(e.message);\n}","tryCatchPattern":"try {\n  const result = await agent.run(...);\n} catch (e) {\n  if (isToolRelayHttpError(e)) {\n    console.error('Relay status/body:', e.message);\n    // restart host relay, then retry the turn\n  } else throw e;\n}","preventionTips":["Health-check TOOL_RELAY_URL before starting each sandbox session.","Keep the host relay process supervised (auto-restart on crash).","Pin the relay port and log every non-2xx response host-side.","Include the tool name and status in host logs to correlate with this error."],"tags":["http","mcp","tool-execution","network"],"backgroundTag":"http-5xx-tool-relay","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}