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

  1. Re-poll /catalog/next to obtain the new revision and tool list, then retry the invocation with the fresh catalogRevision.
  2. After updateCatalog changes the revision, wait for waitForCatalogRefresh (clients ack via /catalog/seen) before dispatching tool calls.
  3. Implement automatic retry-on-409: refresh catalog, look up the tool by name, re-invoke once.
  4. 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

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


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