{"record":{"id":"c2ff16c7dc2ffbcd","repo":"koala73/worldmonitor","slug":"forcereleaselease-no-ramp-configured","errorCode":null,"errorMessage":"[forceReleaseLease] no ramp configured","messagePattern":"\\[forceReleaseLease\\] no ramp configured","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/rampRunner.ts","lineNumber":670,"sourceCode":"/**\n * Operator-only last-resort lease release.\n *\n * Use ONLY when a cron action is genuinely wedged (process died silently\n * between claim and any catch block, leaving a held lease with no\n * partial-failure status). Investigate Convex action logs + Resend dashboard\n * BEFORE calling this — the side effects might have actually completed\n * (segment created, broadcast sent) and the right recovery is then\n * `recoverFromPartialFailure({recovery:\"manual-finished\"})`, not a fresh send.\n *\n * Sets `lastRunStatus = \"partial-failure\"` so `recoverFromPartialFailure`\n * picks up; preserves any `pending*` progress markers so the operator can\n * decide between manual-finished and discard-and-rotate from persisted state.\n */\nexport const forceReleaseLease = internalMutation({\n  args: { reason: v.string() },\n  handler: async (ctx, { reason }) => {\n    const row = await loadConfig(ctx);\n    if (!row) throw new Error(\"[forceReleaseLease] no ramp configured\");\n    if (!row.pendingRunId) {\n      return {\n        ok: true as const,\n        noop: true as const,\n        currentStatus: row.lastRunStatus,\n      };\n    }\n    const releasedRunId = row.pendingRunId;\n    const heldForMs =\n      row.pendingRunStartedAt !== undefined\n        ? Date.now() - row.pendingRunStartedAt\n        : undefined;\n    await ctx.db.patch(row._id, {\n      pendingRunId: undefined,\n      pendingRunStartedAt: undefined,\n      lastRunStatus: \"partial-failure\",\n      lastRunAt: Date.now(),\n      lastRunError: `forced-release: ${reason.slice(0, 200)} (was held by ${releasedRunId}${heldForMs !== undefined ? `, age ${Math.round(heldForMs / 1000)}s` : \"\"})`,","sourceCodeStart":652,"sourceCodeEnd":688,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/broadcast/rampRunner.ts#L652-L688","documentation":"forceReleaseLease is the operator escape hatch that clears a stuck pendingRunId lease and marks the run partial-failure for later recovery. It loads the config via loadConfig and throws if no row exists, so releasing a lease on a non-existent ramp cannot succeed silently.","triggerScenarios":"Calling forceReleaseLease before initRamp, after abortRamp deleted the row, or in an unseeded deployment.","commonSituations":"A lease appears stuck but the ramp was already aborted; tooling pointed at the wrong Convex project.","solutions":["Preflight with getRampStatus; if {configured:false} there is no lease to release.","If abortRamp ran, the lease was deleted with the row — no action needed.","Re-seed via initRamp if a new ramp is intended."],"exampleFix":"// before\nawait ctx.runMutation(internal.broadcast.rampRunner.forceReleaseLease, { reason });\n\n// after\nconst status = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});\nif (!status.configured) return { skipped: 'no ramp configured — no lease to release' };\nawait ctx.runMutation(internal.broadcast.rampRunner.forceReleaseLease, { reason });","handlingStrategy":"validation","validationCode":"// Preflight before forceReleaseLease\nconst status = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});\nif (!status.configured) {\n  return { skipped: 'no ramp configured — no lease to release' };\n}","typeGuard":"function isConfigured(s: { configured: boolean }): s is { configured: true; pendingRunId?: string } {\n  return s.configured === true;\n}","tryCatchPattern":null,"preventionTips":["Preflight forceReleaseLease with getRampStatus.","After abortRamp the lease is gone with the row — no release needed.","Reserve forceReleaseLease for genuinely stuck leases; verify no live run holds it first."],"tags":["convex","broadcast-ramp","state-missing","lease"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}