vercel/ai · error

unauthorized tool relay request

Error message

unauthorized tool relay request

What it means

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.

Source

Thrown at packages/harness-codex/src/bridge/tool-relay.ts:33

  tools,
  emit,
  requestToolResult,
  authorizer = new ToolRelayAuthorizer(),
}: {
  tools: ReadonlyArray<{ name: string }>;
  emit: (message: Record<string, unknown>) => void;
  requestToolResult: (
    toolCallId: string,
  ) => Promise<{ output: unknown; isError?: boolean }>;
  authorizer?: ToolRelayAuthorizer;
}): Promise<ToolRelay> {
  const toolNames = new Set(tools.map(tool => tool.name));
  const pendingCalls = new ToolRelayPendingCalls();

  const server = createServer(async (req, res) => {
    try {
      if (req.method !== 'POST' || req.url !== '/') {
        res.writeHead(401, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: 'unauthorized tool relay request' }));
        return;
      }
      const chunks: Buffer[] = [];
      for await (const chunk of req) {
        chunks.push(chunk as Buffer);
      }
      const body = Buffer.concat(chunks).toString('utf8');
      const { requestId, toolName, input } = JSON.parse(body) as {
        requestId: string;
        toolName: string;
        input: unknown;
      };

      if (!toolNames.has(toolName)) {
        res.writeHead(403, { 'Content-Type': 'application/json' });
        res.end(
          JSON.stringify({ error: `Tool "${toolName}" is not available` }),

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Send POST requests to exactly '/' on the relay server
  2. Fix the client/base URL to point at the relay root without extra path segments
  3. Exclude the relay port from health checks, browser access, or monitoring probes
  4. Verify the relay is started with startAuthorizedToolRelay and that you are targeting the correct port

Example fix

// before
fetch('http://127.0.0.1:PORT/relay/tools'); // wrong path/method
// after
fetch('http://127.0.0.1:PORT/', { method: 'POST', body: JSON.stringify(payload) });
Defensive patterns

Strategy: validation

Validate before calling

const url = new URL(relayBaseUrl);
if (url.pathname !== '/') throw new Error('tool relay requires POST to /');
// then: fetch(relayBaseUrl, { method: 'POST', body })

Try / catch

const res = await fetch(relayUrl, { method: 'POST', body });
if (res.status === 401) {
  const body = await res.json();
  throw new Error(`Relay rejected request: ${body.error}`);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/bb6184b5e27cefa7. Report an issue: GitHub.