{"record":{"id":"b2cf2e4bd2b70835","repo":"koala73/worldmonitor","slug":"clearpartialfailure-no-ramp-configured","errorCode":null,"errorMessage":"[clearPartialFailure] no ramp configured","messagePattern":"\\[clearPartialFailure\\] no ramp configured","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/rampRunner.ts","lineNumber":297,"sourceCode":" * audience tables AND verify no broadcast in the Resend dashboard before\n * calling.\n *\n * For ANY case where the export ran (segment created, contacts stamped,\n * broadcast created, or send fired), use `recoverFromPartialFailure` —\n * `manual-finished` if the wave actually went out, `discard-and-rotate` if\n * not.\n */\nexport const clearPartialFailure = internalMutation({\n  args: {\n    reason: v.string(),\n    // Literal-true forces the operator to actively assert \"I have verified no\n    // export happened.\" A future caller can't accidentally pass `false` to\n    // bypass this.\n    confirmNoExport: v.literal(true),\n  },\n  handler: async (ctx, { reason }) => {\n    const row = await loadConfig(ctx);\n    if (!row) throw new Error(\"[clearPartialFailure] no ramp configured\");\n    if (row.lastRunStatus !== \"partial-failure\") {\n      return {\n        ok: true as const,\n        noop: true as const,\n        currentStatus: row.lastRunStatus,\n      };\n    }\n    // Fail-closed: if any pending-progress marker exists, the export DID make\n    // progress past `assignAndExportWave` — clearing here would mask a stamped\n    // / sent wave. Force the operator to use recoverFromPartialFailure.\n    if (\n      row.pendingWaveLabel ||\n      row.pendingSegmentId ||\n      row.pendingBroadcastId\n    ) {\n      throw new Error(\n        `[clearPartialFailure] refused: pending progress markers present (waveLabel=${row.pendingWaveLabel ?? \"-\"}, segmentId=${row.pendingSegmentId ?? \"-\"}, broadcastId=${row.pendingBroadcastId ?? \"-\"}). The export DID run; clearing here would mask stamped contacts. Use recoverFromPartialFailure instead.`,\n      );","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/broadcast/rampRunner.ts#L279-L315","documentation":"clearPartialFailure resets a partial-failure run state so a fresh wave can be attempted. It loads the config via loadConfig and throws if no row exists, since there is no partial-failure state to clear on a non-existent ramp.","triggerScenarios":"Calling clearPartialFailure before initRamp, after abortRamp deleted the row, or when the ramp config was never seeded in this deployment.","commonSituations":"Recovery runbook run after the ramp was already aborted; stale automation firing against an unseeded Convex project.","solutions":["Preflight with getRampStatus; if {configured:false} there is nothing to clear.","If the ramp was aborted intentionally, the partial-failure state was deleted with it — no action required.","Re-seed with initRamp for a fresh ramp if a new launch is intended."],"exampleFix":"// before\nawait ctx.runMutation(internal.broadcast.rampRunner.clearPartialFailure, { reason, confirmNoExport: true });\n\n// after\nconst status = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});\nif (!status.configured) return { skipped: 'no ramp configured' };\nawait ctx.runMutation(internal.broadcast.rampRunner.clearPartialFailure, { reason, confirmNoExport: true });","handlingStrategy":"validation","validationCode":"// Preflight before clearPartialFailure\nconst status = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});\nif (!status.configured) {\n  return { skipped: 'no ramp configured — nothing to clear' };\n}","typeGuard":"function isConfigured(s: { configured: boolean }): s is { configured: true; lastRunStatus?: string } {\n  return s.configured === true;\n}","tryCatchPattern":null,"preventionTips":["Preflight clearPartialFailure with getRampStatus.","After abortRamp, partial-failure state is deleted with the row — skip the clear step.","Re-seed with initRamp only if a fresh launch is intended."],"tags":["convex","broadcast-ramp","state-missing","partial-failure"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}