koala73/worldmonitor · error · ConvexError
ACCOUNT_NOT_FOUND
Error message
ACCOUNT_NOT_FOUND
What it means
Thrown by markAccountDeleted (accounts.ts:427) when no companyMonitoringAccounts row matches the provided ownerAccountId (matched via by_logicalAccountId index with .unique()), or when the matched row has no ownerUserId. This is the account-deletion entry point triggered with a logical account id, and it refuses to proceed if the target does not exist or is not owner-bound.
Source
Thrown at convex/companyMonitoring/accounts.ts:427
});
await scheduleScopedKeyCacheInvalidation(ctx, ownerUserId);
await scheduleAccountPurge(ctx, ownerFenceHash, purgeGeneration);
return ctx.db.get(account._id);
}
export const markOwnerDeleted = internalMutation({
args: { ownerUserId: v.string() },
handler: async (ctx, args) => terminalize(ctx, args.ownerUserId, "owner_deleted"),
});
export const markAccountDeleted = internalMutation({
args: { ownerAccountId: v.string() },
handler: async (ctx, args) => {
const account = await ctx.db
.query("companyMonitoringAccounts")
.withIndex("by_logicalAccountId", (q) => q.eq("logicalAccountId", args.ownerAccountId))
.unique();
if (!account || !account.ownerUserId) throw new ConvexError("ACCOUNT_NOT_FOUND");
return terminalize(ctx, account.ownerUserId, "account_deleted", account);
},
});
export const advanceAccountPurge = internalMutation({
args: { ownerFenceHash: v.string(), purgeGeneration: v.number() },
handler: async (ctx, args) => {
const account = await ctx.db
.query("companyMonitoringAccounts")
.withIndex("by_ownerFenceHash", (q) => q.eq("ownerFenceHash", args.ownerFenceHash))
.unique();
if (!account || account.purgeGeneration !== args.purgeGeneration) return { status: "stale" };
if (account.purgePhase === "none" || account.purgePhase === "complete") {
return { status: "complete" };
}
const now = Date.now();
if (account.purgePhase === "pending") {View on GitHub (pinned to ffec79ac33)
Solutions
- Verify the ownerAccountId exists: query companyMonitoringAccounts by logicalAccountId via the dashboard or `npx convex run`.
- If the row is already terminalized (ownerUserId stripped, terminalReason set), the deletion is already complete — treat as idempotent success and do not retry.
- If the id is wrong, obtain the correct logicalAccountId from the account row for the intended owner.
- Make the deletion-triggering caller idempotent: if ACCOUNT_NOT_FOUND, check whether the row is already terminal before surfacing an error.
Defensive patterns
Strategy: validation
Validate before calling
// Before calling markAccountDeleted, verify the account exists and is not already terminal:
const account = await ctx.db
.query("companyMonitoringAccounts")
.withIndex("by_logicalAccountId", (q) => q.eq("logicalAccountId", ownerAccountId))
.unique();
if (!account) {
// already gone or never existed — treat as idempotent success
return { status: "already-absent" };
}
if (!account.ownerUserId) {
// already terminalized — nothing to do
return { status: "already-terminal" };
} Try / catch
try {
await ctx.runMutation(internal.companyMonitoring.accounts.markAccountDeleted, { ownerAccountId });
} catch (err) {
if (err instanceof ConvexError && err.message === "ACCOUNT_NOT_FOUND") {
// idempotent: account already deleted or never existed — log and continue
return { status: "not-found" };
}
throw err;
} Prevention
- Make deletion-triggering callers idempotent: treat ACCOUNT_NOT_FOUND as 'already deleted', not an error.
- Verify the logicalAccountId exists before calling markAccountDeleted if you need to distinguish 'never existed' from 'already terminal'.
- Pass the correct logicalAccountId (cm_account_...) from the caller, not a Convex _id or ownerUserId.
When it happens
Trigger: Calling markAccountDeleted with an ownerAccountId (logical id like 'cm_account_...') that was never created, was already deleted, or has a typo. The row exists but its ownerUserId field is null/undefined (already terminalized — terminal rows strip ownerUserId).
Common situations: A deletion webhook fires for an account that was already terminalized by a prior deletion event. The ownerAccountId was transcribed incorrectly. The account row was manually deleted from the DB. A retry of a deletion event arrives after the account is already gone.
Related errors
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/ddacbb96353521a0.
Report an issue: GitHub.