koala73/worldmonitor · error · ConvexError
ALREADY_REVOKED
ALREADY_REVOKED
Error message
ALREADY_REVOKED
What it means
A ConvexError thrown by the revokeApiKey mutation when the target userApiKeys document exists, belongs to the calling user, but already has a non-null revokedAt timestamp. It fires when a client attempts to revoke an already-revoked API key, guarding against double-revocation and overwriting the original revocation time. It is a sentinel guard; the input at fault is the keyId of a previously revoked key. Clients should treat this key as already inactive rather than retrying.
Solutions
- Treat the error as success — the desired end state (revoked) already holds
- Have callers check revokedAt before calling revoke, or catch ALREADY_REVOKED and return a no-op success
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at convex/apiKeys.ts:183 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/29e9d1f0ed074b0f.
Report an issue: GitHub.
Appendix: source
Thrown at convex/apiKeys.ts:183
revokedAt: k.revokedAt,
scopes: k.scopes,
companyMonitoringAccountId: k.companyMonitoringAccountId,
}));
},
});
/** Revoke a key owned by the current user. */
export const revokeApiKey = mutation({
args: { keyId: v.id("userApiKeys") },
handler: async (ctx, args) => {
const userId = await requireUserId(ctx);
const key = await ctx.db.get(args.keyId);
if (!key || key.userId !== userId) {
throw new ConvexError("NOT_FOUND");
}
if (key.revokedAt) {
throw new ConvexError("ALREADY_REVOKED");
}
await ctx.db.patch(args.keyId, { revokedAt: Date.now() });
return { ok: true, keyHash: key.keyHash };
},
});
// ---------------------------------------------------------------------------
// Internal (service-to-service) — called from HTTP actions / middleware
// ---------------------------------------------------------------------------
/**
* Look up an API key by its SHA-256 hash.
* Returns the key row (with userId) if found and not revoked, else null.
* Used by the edge gateway to validate incoming API keys.
*/
export const validateKeyByHash = internalQuery({
args: { keyHash: v.string() },View on GitHub (pinned to eeab0a219f)