vercel/ai · error · RelayRequestError
Invalid host tool relay credential.
Error message
Invalid host tool relay credential.
What it means
Every relay request must carry an Authorization header equal to 'Bearer ' + the random 32-byte credential returned by startHostToolRelay. The comparison is a timing-safe constant-time check of the exact value (including the Bearer prefix). A missing, malformed, or wrong header produces this 401 RelayRequestError before any routing happens.
Source
Thrown at packages/harness-acp/src/v1/bridge/host-tool-relay.ts:172
credential: string;
state: CatalogState;
serverName: string;
turn: HostToolRelayTurn | undefined;
nextInvocationOrder: () => number;
}): Promise<unknown> {
if (request.method !== 'POST') {
throw new RelayRequestError({
status: 404,
message: 'Unknown host tool relay endpoint.',
});
}
if (
!credentialsMatch({
expected: credential,
actual: request.headers.authorization,
})
) {
throw new RelayRequestError({
status: 401,
message: 'Invalid host tool relay credential.',
});
}
const body = await readJSONBody({ request });
if (request.url === '/catalog/next') {
return handleCatalogNext({ body, state });
}
if (request.url === '/catalog/seen') {
return handleCatalogSeen({ body, state });
}
if (request.url === '/invoke') {
return handleInvocation({
body,
state,
serverName,
turn,
nextInvocationOrder,View on GitHub (pinned to 69428b1f8b)
Solutions
- Set the header to exactly `Bearer ${credential}` using the credential from the same startHostToolRelay call that produced the URL.
- Check the header is not being stripped by middleware, proxy, or fetch redirect (credentials are not forwarded cross-origin).
- If the host process restarted, re-fetch a fresh URL/credential pair instead of reusing cached ones.
- Debug by logging request.headers.authorization on the client side to confirm the exact value sent.
Example fix
// before
headers: { authorization: credential }
// after
headers: { authorization: `Bearer ${relay.credential}` } Defensive patterns
Strategy: try-catch
Validate before calling
function assertCredential(relay: { url: string; credential: string }) {
if (typeof relay.credential !== 'string' || relay.credential.length === 0) {
throw new Error('Missing relay credential — use the value returned by startHostToolRelay.');
}
} Type guard
function hasAuthHeader(headers: Record<string, string>): boolean {
return typeof headers.authorization === 'string' &&
headers.authorization.startsWith('Bearer ') &&
headers.authorization.length > 'Bearer '.length;
} Try / catch
const res = await fetch(url, { headers: { authorization: `Bearer ${credential}` } });
if (res.status === 401) {
const { error } = await res.json();
if (error === 'Invalid host tool relay credential.') {
// re-acquire url+credential from the current relay instance and retry
}
} Prevention
- Always read url and credential from the same HostToolRelay object — never hardcode.
- Include the 'Bearer ' prefix exactly.
- After host restart, re-fetch the credential instead of caching it.
- Check proxies/middleware for Authorization header stripping before debugging the library.
When it happens
Trigger: Posting to /invoke, /catalog/next, or /catalog/seen without the authorization header, with a token missing the 'Bearer ' prefix, with a credential from a different relay instance, or after restarting the process so the old credential no longer matches.
Common situations: Hardcoding a stale credential instead of using the credential returned from startHostToolRelay; forgetting the Bearer prefix; forwarding requests through a proxy that strips the Authorization header; pointing a client at a second relay instance's URL while using the first instance's credential.
Related errors
- Unknown host tool relay endpoint.
- ${readErrorMessage({ value, status: response.status })}
- A host tool relay turn is already active.
- Invalid host tool catalog poll request.
- Invalid host tool catalog acknowledgment.
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/84088d0a0d9c861e.
Report an issue: GitHub.