vercel/ai · error · RelayRequestError

Host tool relay request is not valid JSON.

Error message

Host tool relay request is not valid JSON.

What it means

readJSONBody concatenates the raw request chunks and parses them with Response.json(); when parsing fails it throws RelayRequestError with HTTP status 400. The relay endpoint only accepts well-formed JSON request bodies, so any malformed body is rejected before it reaches tool dispatch. This surfaces protocol misuse by the caller of the relay endpoint rather than a tool failure.

Source

Thrown at packages/harness-acp/src/v1/bridge/host-tool-relay.ts:457

  let size = 0;
  for await (const chunk of request) {
    const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
    size += buffer.length;
    if (size > 16 * 1024 * 1024) {
      throw new RelayRequestError({
        status: 413,
        message: 'Host tool relay request is too large.',
      });
    }
    chunks.push(buffer);
  }
  const text = Buffer.concat(chunks).toString('utf8');
  try {
    return await new Response(text, {
      headers: { 'content-type': 'application/json' },
    }).json();
  } catch {
    throw new RelayRequestError({
      status: 400,
      message: 'Host tool relay request is not valid JSON.',
    });
  }
}

function credentialsMatch({
  expected,
  actual,
}: {
  expected: string;
  actual: string | undefined;
}): boolean {
  const expectedValue = Buffer.from(`Bearer ${expected}`);
  const actualValue = Buffer.from(actual ?? '');
  return (
    expectedValue.length === actualValue.length &&
    timingSafeEqual(expectedValue, actualValue)

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Serialize the request body with JSON.stringify and set the content-type header to application/json before posting to the relay.
  2. Log/inspect the exact body text being sent and validate it with safeParseJSON before sending.
  3. Check that the client fully writes the body and does not abort mid-stream, producing truncated JSON.

Example fix

// before
await fetch(relayUrl, { method: 'POST', body: params }); // params is an object
// after
await fetch(relayUrl, {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify(params),
});
Defensive patterns

Strategy: validation

Validate before calling

import { safeParseJSON } from '@ai-sdk/provider-utils';
const text = JSON.stringify(body);
if (safeParseJSON(text) == null) throw new Error('Relay body is not valid JSON');

Try / catch

try {
  await postToRelay(body);
} catch (error) {
  if (RelayRequestError.isInstance(error) && error.status === 400) {
    // log the serialized body and fix serialization
  }
  throw error;
}

Prevention

When it happens

Trigger: A request to the host tool relay endpoint whose body is empty, truncated, not UTF-8 JSON (e.g. form-encoded or binary data), or syntactically invalid JSON, causing Response(...).json() to throw inside readJSONBody.

Common situations: Client sends URL-encoded or multipart body instead of application/json; request stream cut off mid-write producing truncated JSON; manual fetch calls constructing the body with string concatenation that produces invalid JSON (unquoted keys, trailing commas); sending NDJSON lines that are not a single JSON document.

Related errors


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