vercel/ai · error · Error

${readErrorMessage({ value, status: response.status })}

Error message

${readErrorMessage({ value, status: response.status })}

What it means

In postRelay, when the host tool relay responds with a non-ok HTTP status, the error thrown carries the relay's own error message (from the JSON body's 'error' field) or falls back to 'Host tool relay returned HTTP <status>.'. This surfaces server-side failures of /invoke, /catalog/next, and /catalog/seen calls to the caller.

Source

Thrown at packages/harness-acp/src/v1/bridge/host-tool-mcp.ts:105

  }
}

async function postRelay({
  path,
  body,
}: {
  path: string;
  body: Readonly<Record<string, unknown>>;
}): Promise<unknown> {
  const response = await postHostToolRelay({
    relayUrl,
    relayCredential,
    path,
    body,
  });
  const { value } = response;
  if (!response.ok) {
    throw new Error(readErrorMessage({ value, status: response.status }));
  }
  return value;
}

function validateToolCatalog({
  value,
}: {
  value: unknown;
}): ReadonlyArray<HarnessV1BridgeToolWire> {
  if (!Array.isArray(value) || !value.every(isTool)) {
    throw new Error('Invalid host tool catalog.');
  }
  return value;
}

async function readToolCatalog({
  path,
}: {

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Read the thrown message: it is the relay's own 'error' field, so fix the underlying relay-reported condition first.
  2. If the message is the generic 'Host tool relay returned HTTP <status>', check the relay logs for that status code.
  3. Verify AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL is current and AI_SDK_ACP_HOST_TOOL_RELAY_URL is correct.
  4. For invoke failures, confirm the tool still exists in the catalog file and that the bridge's catalogRevision is fresh (restart the bridge to resync).
  5. Confirm the relay service is healthy and reachable.

Example fix

// before: stale credential
AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL=old-token
// after
AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL=<freshly issued relay token>
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight check before starting heavy work:
const res = await fetch(relayUrl + '/catalog/seen', { method: 'POST', headers: { authorization: relayCredential }, body: JSON.stringify({ revision: 1 }) });
if (!res.ok) throw new Error(`Relay pre-flight failed: HTTP ${res.status}`);

Try / catch

try {
  await postRelay({ path: '/invoke', body });
} catch (error) {
  const message = error instanceof Error ? error.message : String(error);
  if (/HTTP 40[13]/.test(message)) {
    // refresh AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL and retry once
  } else {
    // surface the relay's error message to the MCP caller
  }
}

Prevention

When it happens

Trigger: Any postHostToolRelay call (from hostToolServer invoke, watchCatalog poll, or onListTools) where the relay returns HTTP >= 400: invalid relay credential (401/403), unknown toolName or stale catalogRevision on /invoke (4xx), relay internal error (5xx), or body without an 'error' string field.

Common situations: Expired or rotated AI_SDK_ACP_HOST_TOOL_RELAY_CREDENTIAL; invoking a tool that was removed from the host catalog; sending a catalogRevision the relay no longer accepts; relay deployment down or crashing on request; wrong AI_SDK_ACP_HOST_TOOL_RELAY_URL pointing at a different service.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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