vercel/ai · warning · RelayRequestError
Invalid host tool catalog poll request.
Error message
Invalid host tool catalog poll request.
What it means
The /catalog/next long-poll endpoint requires a JSON body that is an object with an afterRevision field that is a safe non-negative integer. Anything else — missing field, non-integer, negative, or a non-object body — yields this 400 RelayRequestError before the poll is evaluated.
Source
Thrown at packages/harness-acp/src/v1/bridge/host-tool-relay.ts:211
throw new RelayRequestError({
status: 404,
message: 'Unknown host tool relay endpoint.',
});
}
async function handleCatalogNext({
body,
state,
}: {
body: unknown;
state: CatalogState;
}): Promise<unknown> {
if (
!isRecord(body) ||
!Number.isSafeInteger(body.afterRevision) ||
(body.afterRevision as number) < 0
) {
throw new RelayRequestError({
status: 400,
message: 'Invalid host tool catalog poll request.',
});
}
const afterRevision = body.afterRevision as number;
if (!state.closed && afterRevision >= state.revision) {
await waitForCatalogChange({ state });
}
if (state.closed) {
return { closed: true, revision: state.revision };
}
return afterRevision < state.revision
? { revision: state.revision, tools: state.tools }
: { revision: state.revision };
}
function handleCatalogSeen({
body,View on GitHub (pinned to 69428b1f8b)
Solutions
- Send { afterRevision: <non-negative safe integer> } — use 0 for the initial poll.
- Ensure the body is JSON with content-type application/json and no extra top-level type mismatch (must be an object).
- Validate the value client-side with Number.isSafeInteger and >= 0 before sending.
- If revisions grow beyond safe integers in your client, stop and re-sync rather than casting.
Example fix
// before
body: JSON.stringify({ revision: lastRev })
// after
body: JSON.stringify({ afterRevision: Number.isSafeInteger(lastRev) && lastRev >= 0 ? lastRev : 0 }) Defensive patterns
Strategy: validation
Validate before calling
function catalogPollBody(afterRevision: unknown) {
if (!Number.isSafeInteger(afterRevision) || (afterRevision as number) < 0) {
throw new Error('afterRevision must be a non-negative safe integer');
}
return JSON.stringify({ afterRevision });
} Type guard
function isValidPollBody(body: unknown): body is { afterRevision: number } {
return body != null && typeof body === 'object' && !Array.isArray(body) &&
Number.isSafeInteger((body as any).afterRevision) &&
(body as any).afterRevision >= 0;
} Prevention
- Initial poll should use { afterRevision: 0 }.
- Never send the revision as a string; JSON numbers only.
- Use the field name afterRevision (not revision) for polls.
- Validate with Number.isSafeInteger before serializing.
When it happens
Trigger: Polling /catalog/next with an empty body, with { revision: 0 } instead of { afterRevision: 0 }, with afterRevision as a string ('0'), a float, null, or a negative number, or sending non-JSON content.
Common situations: Hand-rolled polling clients using the wrong field name; JSON that lost integer precision (afterRevision sent as 1.0 string); test scripts hitting the endpoint directly without the expected payload shape; serializing undefined so the field is dropped.
Related errors
- Invalid host tool catalog acknowledgment.
- Invalid host tool relay request.
- Host tool ${body.toolName} was invoked from stale catalog re
- Host tool ${body.toolName} is not active in catalog revision
- Invalid host tool catalog poll response.
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/6c69bb4982cbf244.
Report an issue: GitHub.