vercel/ai · error · RelayRequestError
Host tool ${body.toolName} was invoked from stale catalog re
Error message
Host tool ${body.toolName} was invoked from stale catalog revision ${body.catalogRevision}; the active revision is ${state.revision}. What it means
Host tool invocations are pinned to a catalog revision: the catalogRevision sent in the /invoke body must equal the relay's current state.revision. If the tool catalog changed (updateCatalog bumped the revision) since the client last fetched it, the relay rejects with this 409 so a stale tool definition is never executed.
Source
Thrown at packages/harness-acp/src/v1/bridge/host-tool-relay.ts:290
throw new RelayRequestError({
status: 409,
message: 'No ACP prompt turn is active.',
});
}
if (
!isRecord(body) ||
typeof body.requestId !== 'string' ||
typeof body.toolName !== 'string' ||
!isRecord(body.input) ||
!Number.isSafeInteger(body.catalogRevision)
) {
throw new RelayRequestError({
status: 400,
message: 'Invalid host tool relay request.',
});
}
if (body.catalogRevision !== state.revision) {
throw new RelayRequestError({
status: 409,
message:
`Host tool ${body.toolName} was invoked from stale catalog revision ` +
`${body.catalogRevision}; the active revision is ${state.revision}.`,
});
}
const tool = state.tools.find(item => item.name === body.toolName);
if (tool == null) {
throw new RelayRequestError({
status: 404,
message:
`Host tool ${body.toolName} is not active in catalog revision ` +
`${state.revision}.`,
});
}
const correlationToken = randomBytes(32).toString('hex');
turn.registerCorrelationInvocation({View on GitHub (pinned to 69428b1f8b)
Solutions
- Re-poll /catalog/next to obtain the new revision and tool list, then retry the invocation with the fresh catalogRevision.
- After updateCatalog changes the revision, wait for waitForCatalogRefresh (clients ack via /catalog/seen) before dispatching tool calls.
- Implement automatic retry-on-409: refresh catalog, look up the tool by name, re-invoke once.
- Avoid changing the tool set mid-turn; batch tool updates between turns.
Example fix
// before
await post(invokeUrl, { requestId, toolName, input, catalogRevision: cachedRev });
// after
let res = await post(invokeUrl, { requestId, toolName, input, catalogRevision: cachedRev });
if (res.status === 409) {
const catalog = await pollCatalog(cachedRev); // GET new revision via /catalog/next
cachedRev = catalog.revision;
res = await post(invokeUrl, { requestId, toolName, input, catalogRevision: cachedRev });
} Defensive patterns
Strategy: retry
Validate before calling
// Check freshness before invoking
async function ensureFreshRevision(cached: number, poll: (after: number) => Promise<{ revision: number }>) {
const latest = await poll(cached);
if (latest.revision !== cached) {
throw new Error(`Catalog changed: cached ${cached}, current ${latest.revision}. Re-fetch tools before invoking.`);
}
return cached;
} Try / catch
let res = await postJson(invokeUrl, { ...body, catalogRevision: cachedRev });
if (res.status === 409 && /stale catalog revision/.test((await res.json()).error ?? '')) {
const next = await pollCatalog(cachedRev); // long-poll /catalog/next
cachedRev = next.revision;
res = await postJson(invokeUrl, { ...body, catalogRevision: cachedRev }); // retry once
} Prevention
- Re-poll /catalog/next immediately after any tool-set change notification.
- Use waitForCatalogRefresh / /catalog/seen handshake before resuming invocations after updates.
- Avoid mutating the tool catalog mid-turn.
- Track the revision returned with every catalog fetch and always send that exact value.
When it happens
Trigger: The host updated its tool list (updateCatalog produced changed: true, revision+1) while the MCP client still holds the old catalog and invokes with the old catalogRevision; a slow/parked client resumes mid-turn after a tool re-registration; multiple clients with out-of-sync revisions.
Common situations: Dynamic tool registration/removal during an active session; agent process restarted tools mid-conversation; client long-poll lag — it invoked before polling /catalog/next for the new revision; ignoring the waitForCatalogRefresh signal.
Related errors
- A host tool relay turn is already active.
- Invalid host tool catalog poll request.
- Invalid host tool catalog acknowledgment.
- Host tool ${body.toolName} is not active in catalog revision
- Unknown host tool relay endpoint.
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/0952cbbdcdff1728.
Report an issue: GitHub.