{"record":{"id":"bb6184b5e27cefa7","repo":"vercel/ai","slug":"unauthorized-tool-relay-request","errorCode":null,"errorMessage":"unauthorized tool relay request","messagePattern":"unauthorized tool relay request","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"packages/harness-codex/src/bridge/tool-relay.ts","lineNumber":33,"sourceCode":"  tools,\n  emit,\n  requestToolResult,\n  authorizer = new ToolRelayAuthorizer(),\n}: {\n  tools: ReadonlyArray<{ name: string }>;\n  emit: (message: Record<string, unknown>) => void;\n  requestToolResult: (\n    toolCallId: string,\n  ) => Promise<{ output: unknown; isError?: boolean }>;\n  authorizer?: ToolRelayAuthorizer;\n}): Promise<ToolRelay> {\n  const toolNames = new Set(tools.map(tool => tool.name));\n  const pendingCalls = new ToolRelayPendingCalls();\n\n  const server = createServer(async (req, res) => {\n    try {\n      if (req.method !== 'POST' || req.url !== '/') {\n        res.writeHead(401, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ error: 'unauthorized tool relay request' }));\n        return;\n      }\n      const chunks: Buffer[] = [];\n      for await (const chunk of req) {\n        chunks.push(chunk as Buffer);\n      }\n      const body = Buffer.concat(chunks).toString('utf8');\n      const { requestId, toolName, input } = JSON.parse(body) as {\n        requestId: string;\n        toolName: string;\n        input: unknown;\n      };\n\n      if (!toolNames.has(toolName)) {\n        res.writeHead(403, { 'Content-Type': 'application/json' });\n        res.end(\n          JSON.stringify({ error: `Tool \"${toolName}\" is not available` }),","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-codex/src/bridge/tool-relay.ts#L15-L51","documentation":"The Codex harness tool relay is a local HTTP server that authorizes by requiring exact POST requests to '/'. Any other method or path is rejected with a 401 and the JSON body { error: 'unauthorized tool relay request' }. It is an authorization gate protecting the relay endpoint.","triggerScenarios":"Sending GET/PUT or hitting any path other than '/' on the relay server port; a health check or browser prefetch hitting the server; a misconfigured client URL including a path suffix.","commonSituations":"Probing the relay port from a browser (GET /favicon.ico); load balancers or monitoring doing GET health checks; tools pointed at a base URL with a trailing path instead of the root endpoint.","solutions":["Send POST requests to exactly '/' on the relay server","Fix the client/base URL to point at the relay root without extra path segments","Exclude the relay port from health checks, browser access, or monitoring probes","Verify the relay is started with startAuthorizedToolRelay and that you are targeting the correct port"],"exampleFix":"// before\nfetch('http://127.0.0.1:PORT/relay/tools'); // wrong path/method\n// after\nfetch('http://127.0.0.1:PORT/', { method: 'POST', body: JSON.stringify(payload) });","handlingStrategy":"validation","validationCode":"const url = new URL(relayBaseUrl);\nif (url.pathname !== '/') throw new Error('tool relay requires POST to /');\n// then: fetch(relayBaseUrl, { method: 'POST', body })","typeGuard":null,"tryCatchPattern":"const res = await fetch(relayUrl, { method: 'POST', body });\nif (res.status === 401) {\n  const body = await res.json();\n  throw new Error(`Relay rejected request: ${body.error}`);\n}","preventionTips":["POST only to the relay root path '/'","Strip trailing paths from the configured relay base URL","Exclude the relay port from monitoring/health-check probes"],"tags":["http","authorization","relay"],"backgroundTag":"http-401-unauthorized","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}