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

  1. Treat the error as success — the desired end state (revoked) already holds
  2. 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)