{"record":{"id":"e9727c4f5a77917d","repo":"koala73/worldmonitor","slug":"rundailyramp-rampcurve-nexttier-is-undefined","errorCode":null,"errorMessage":"[runDailyRamp] rampCurve[${nextTier}] is undefined despite bounds check — config corruption?","messagePattern":"\\[runDailyRamp\\] rampCurve\\[(.+?)\\] is undefined despite bounds check — config corruption\\?","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/rampRunner.ts","lineNumber":1027,"sourceCode":"      }\n    }\n\n    // ──── Step 2: figure out which tier to send next ────\n    const nextTier = row.currentTier + 1;\n    if (nextTier >= row.rampCurve.length) {\n      console.log(\"[runDailyRamp] ramp curve complete — deactivating\");\n      await ctx.runMutation(\n        internal.broadcast.rampRunner._recordRunOutcome,\n        { status: \"ramp-complete\", deactivate: true },\n      );\n      return { status: \"ramp-complete\" };\n    }\n    // Bounds-checked above; explicit guard quiets noUncheckedIndexedAccess\n    // and protects against a future code change that breaks the\n    // bounds check above without realising this index is now unsafe.\n    const count = row.rampCurve[nextTier];\n    if (count === undefined) {\n      throw new Error(\n        `[runDailyRamp] rampCurve[${nextTier}] is undefined despite bounds check — config corruption?`,\n      );\n    }\n    const waveLabel = `${row.waveLabelPrefix}-${nextTier + row.waveLabelOffset}`;\n\n    // ──── Step 3: in-flight guard via the new wave-loading state machine ────\n    // PR 2 (post-launch-stabilization plan, 2026-04-29) replaces the\n    // monolithic assignAndExportWave + createProLaunchBroadcast + sendProLaunchBroadcast\n    // chain with a self-driving multi-step pipeline that fits within the\n    // Convex 10-min action runtime budget at any wave size. The state lives\n    // on `waveRuns` + `wavePickedContacts`. See `convex/broadcast/waveRuns.ts`.\n    //\n    // The legacy `_claimTierForRun` + `_recordPendingExport` + `_recordPendingBroadcast`\n    // + `_recordWaveSent` + `_recordRunOutcome` mutations remain in this\n    // module so the operator-recovery commands (`recoverFromPartialFailure`,\n    // `clearPartialFailure`, `forceReleaseLease`) keep working on any\n    // legacy partial-failure rows. New runs go through the state machine.\n    //","sourceCodeStart":1009,"sourceCodeEnd":1045,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/convex/broadcast/rampRunner.ts#L1009-L1045","documentation":"Defensive invariant guard in runDailyRamp: rampCurve[nextTier] is undefined even though the nextTier < rampCurve.length bounds check just passed. This should be unreachable and indicates the persisted broadcastRampConfig.rampCurve array is corrupted — mutated between the check and the access, or written with holes.","triggerScenarios":"Thrown at convex/broadcast/rampRunner.ts:1025 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Inspect the broadcastRampConfig document's rampCurve array for holes, non-numeric entries, or concurrent modification","Re-run configureRamp to rewrite a clean rampCurve and resume the ramp","Add a validation step in configureRamp that rejects sparse or malformed rampCurve arrays before they persist"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}