koala73/worldmonitor · error · ConvexError

activation outcome buckets must be disjoint and contain at m

Error message

activation outcome buckets must be disjoint and contain at most ${proActivationStepIdValidator.members.length} steps

What it means

ConvexError guard validating the Pro activation outcome step buckets: confirmedSteps, skippedSteps, blockedSteps, and failedSteps combined must contain no duplicates and no more entries than the total number of known activation steps. Fires when a client double-reports a step in two buckets or fabricates unknown/duplicate step ids.

Source

Thrown at convex/payments/billing.ts:997

      !Number.isSafeInteger(args.revision) ||
      args.revision < 1 ||
      args.revision > MAX_PRO_ACTIVATION_OUTCOME_REVISION
    ) {
      throw new ConvexError(
        `activation outcome revision must be an integer from 1 to ${MAX_PRO_ACTIVATION_OUTCOME_REVISION}`,
      );
    }
    const allSteps = [
      ...args.confirmedSteps,
      ...args.skippedSteps,
      ...(args.blockedSteps ?? []),
      ...args.failedSteps,
    ];
    if (
      allSteps.length > proActivationStepIdValidator.members.length ||
      new Set(allSteps).size !== allSteps.length
    ) {
      throw new ConvexError(
        `activation outcome buckets must be disjoint and contain at most ${proActivationStepIdValidator.members.length} steps`,
      );
    }
    if (args.revision <= (presentation.outcomeRevision ?? 0)) {
      return false;
    }

    const now = Date.now();
    await ctx.db.patch(presentation._id, {
      confirmedSteps: args.confirmedSteps,
      skippedSteps: args.skippedSteps,
      // Written straight from the arg, NOT coerced to []. Convex removes a
      // field patched as `undefined`, so a client too old to report blocked
      // steps leaves the field ABSENT ("could not report") instead of claiming
      // "none were blocked" — a claim that is actively false for that client,
      // which classified denials as skips. Absent is also what lets an analyst
      // exclude those rows from a denial rate. Every snapshot stays a full
      // replacement either way: an explicit [] clears, and so does absence.

View on GitHub (pinned to 9361220cc0)

Solutions

  1. Place each step id in exactly one outcome bucket per revision
  2. Use only step ids from proActivationStepIdValidator's known members
  3. Rebuild the buckets client-side from the actual per-step outcomes before submitting
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at convex/payments/billing.ts:981 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of koala73/worldmonitor@9361220cc0 (2026-08-21). Data as JSON: /api/errors/0c8ac9ed0bea28c8. Report an issue: GitHub.