{"record":{"id":"b3837ef655055e66","repo":"koala73/worldmonitor","slug":"activation-session-nonce-cannot-change-its-start-o","errorCode":null,"errorMessage":"activation session nonce cannot change its start order","messagePattern":"activation session nonce cannot change its start order","errorType":"validation","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/payments/billing.ts","lineNumber":879,"sourceCode":"          : {}),\n        // Day-0 has no confirm handshake, so presentation is recorded here —\n        // before the interstitial renders — to keep a subscriber who closes\n        // the tab immediately inside the cohort instead of invisible.\n        presentedAt: now,\n        outcomeTrackingVersion: PRO_ACTIVATION_OUTCOME_TRACKING_VERSION,\n      });\n      return { status: \"opened\" as const };\n    }\n    if (existing.exitedAt !== undefined) {\n      return { status: \"already_recorded\" as const };\n    }\n    if (existing.claimNonce === args.claimNonce) {\n      if (\n        existing.sessionStartedAt !== undefined &&\n        args.sessionStartedAt !== undefined &&\n        existing.sessionStartedAt !== args.sessionStartedAt\n      ) {\n        throw new ConvexError(\n          \"activation session nonce cannot change its start order\",\n        );\n      }\n      // Mixed-deploy compatibility: attach the explicit order to an unfinished\n      // row opened by this same session before the field was deployed.\n      if (\n        existing.sessionStartedAt === undefined &&\n        args.sessionStartedAt !== undefined\n      ) {\n        await ctx.db.patch(existing._id, {\n          sessionStartedAt: args.sessionStartedAt,\n        });\n      }\n      return { status: \"opened\" as const };\n    }\n    if (existing.claimNonce !== args.claimNonce) {\n      // A cached client without sessionStartedAt can replay its own nonce (the\n      // branch above) but cannot establish that a different session is newer,","sourceCodeStart":861,"sourceCodeEnd":897,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/convex/payments/billing.ts#L861-L897","documentation":"ConvexError guard enforcing that a resuming Pro activation session cannot change its sessionStartedAt while keeping the same claim nonce. The nonce pins the session's start ordering; a retry that mutates the start timestamp for an already-recorded nonce is inconsistent (possible replay/tamper) and is rejected.","triggerScenarios":"Thrown at convex/payments/billing.ts:863 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Resend the exact same sessionStartedAt value that was used when the session was first opened","Generate a fresh claimNonce (new session) instead of changing the timestamp of an existing one","Fix client state persistence so session parameters survive reloads unchanged"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"9361220cc013571781071f0206e4d80fd14b2f7f","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}