koala73/worldmonitor · error · ConvexError

NOTICE_NOT_FOUND

NOTICE_NOT_FOUND

Error message

NOTICE_NOT_FOUND

What it means

Thrown by the acknowledgeNotice mutation: no apiPlanLimitNotices document matching the supplied noticeId exists for the current user (or at all). The acknowledgement cannot proceed because there is nothing to acknowledge.

Solutions

  1. Re-fetch the pending notices list and acknowledge a current noticeId
  2. Drop stale notice IDs in the client when the list refreshes so users cannot click outdated acknowledgements
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at convex/apiPlanLimitNotices.ts:431 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/7d86d8f889a6b40c. Report an issue: GitHub.

Appendix: source

Thrown at convex/apiPlanLimitNotices.ts:431

        .collect();
      notices.push(...rows.filter((notice) => notice.acknowledgedAt === undefined));
    }
    return notices.sort((a, b) => {
      const severity = (state: ApiPlanLimitNoticeState) =>
        state === "over_limit" ? 3 : state === "sustained_burst" ? 2 : 1;
      const severityDiff = severity(b.state) - severity(a.state);
      return severityDiff || b.lastSeenAt - a.lastSeenAt;
    });
  },
});

export const acknowledgeNotice = mutation({
  args: { noticeId: v.id("apiPlanLimitNotices") },
  handler: async (ctx, args) => {
    const userId = await requireUserId(ctx);
    const notice = await ctx.db.get(args.noticeId);
    if (!notice || notice.userId !== userId) {
      throw new ConvexError("NOTICE_NOT_FOUND");
    }
    await ctx.db.patch(args.noticeId, { acknowledgedAt: Date.now() });
    return { ok: true };
  },
});

export const listEmailDue = internalQuery({
  args: {
    now: v.number(),
    limit: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    const max = args.limit ?? 100;
    // Scope to `current` in the INDEX so a backlog of superseded (current:false)
    // pending/failed rows -- which sort first by oldest lastSeenAt -- can never
    // consume the take() budget and starve genuinely-due live notices.
    const pending = await ctx.db
      .query("apiPlanLimitNotices")

View on GitHub (pinned to eeab0a219f)