koala73/worldmonitor · warning · Error
This client was already revoked or no longer exists.
Error message
This client was already revoked or no longer exists.
What it means
Mapped by revokeMcpClient() in src/services/mcp-clients.ts from an HTTP 404 returned by POST /api/user/mcp-revoke. The edge handler translates Convex's NOT_FOUND into 404 and deliberately collapses three server cases for anti-enumeration: the tokenId never existed, the id is malformed, or the row exists but is owned by a different user (the Convex mutation asserts row.userId === caller). It is a deterministic terminal outcome — retrying with the same id will always 404.
Source
Thrown at src/services/mcp-clients.ts:91
* Throws on non-2xx so the UI can surface the error.
*/
export async function revokeMcpClient(tokenId: string): Promise<void> {
const token = await getClerkToken();
if (!token) throw new Error('Sign in to revoke MCP clients.');
const resp = await fetch('/api/user/mcp-revoke', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ tokenId }),
});
if (resp.ok) return;
if (resp.status === 404) {
throw new Error('This client was already revoked or no longer exists.');
}
if (resp.status === 409) {
throw new Error('This client was already revoked.');
}
if (resp.status === 401) {
throw new Error('Sign in to revoke MCP clients.');
}
if (resp.status === 503) {
throw new Error('Revoke service is temporarily unavailable. Try again in a moment.');
}
throw new Error(`Revoke failed (HTTP ${resp.status}).`);
}
/**
* Fetch the caller's daily Pro MCP quota usage. Returns sane defaults on
* any failure — the settings UI is informational and should never break
* because the quota counter is unreachable.
*/View on GitHub (pinned to eeab0a219f)
Solutions
- Refresh the client list (listMcpClients()) and reconcile the UI — drop rows that no longer exist.
- Confirm the tokenId comes from the same deployment's list call (no dev/prod mixing via VITE_CONVEX_URL).
- Treat this outcome as success-equivalent for UX: the client cannot be used, so remove it from the display.
- Do not blindly retry — 404 is authoritative and idempotent.
Example fix
// before
await revokeMcpClient(tokenId);
// after
try {
await revokeMcpClient(tokenId);
} catch (err) {
if (err instanceof Error && err.message.includes('already revoked or no longer exists')) {
removeClientFromUi(tokenId);
return;
}
throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
const clients = await listMcpClients();
const existsAndMine = clients.some((c) => c.id === tokenId && !c.revokedAt);
if (!existsAndMine) {
removeClientFromUi(tokenId); // stale row — nothing to revoke
return;
}
await revokeMcpClient(tokenId); Type guard
function isNotFoundRevoke(err: unknown): boolean {
return err instanceof Error && err.message === 'This client was already revoked or no longer exists.';
} Try / catch
try {
await revokeMcpClient(tokenId);
} catch (err) {
if (isNotFoundRevoke(err)) {
removeClientFromUi(tokenId); // terminal, idempotent — do not retry
return;
}
throw err;
} Prevention
- Refresh the client list before revoking to avoid stale tokenIds.
- Never mix deployments: list and revoke must hit the same Convex backend.
- Remember 404 also covers 'not your row' (anti-enumeration) — do not probe foreign ids.
When it happens
Trigger: Revoking from a stale list where the token was already revoked and pruned server-side; passing a tokenId obtained from a different Convex deployment (dev data against prod); a hand-copied/truncated id; attempting to revoke a row that belongs to another account after a session switch.
Common situations: Two tabs open where one already revoked the client; environments mixed up between local dev and production Convex; token rows removed by an admin/script between listing and revoking.
Related errors
- NOT_FOUND
- CLAIM_NOT_FOUND
- Revoke service is temporarily unavailable. Try again in a mo
- invalid-user-id
- get_intel_timeline requires at least one of domain ("conflic
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/f3347d25673acbe6.
Report an issue: GitHub.