{"record":{"id":"8b7534bcd312af29","repo":"koala73/worldmonitor","slug":"recordpendingexport-no-ramp-configured","errorCode":null,"errorMessage":"[_recordPendingExport] no ramp configured","messagePattern":"\\[_recordPendingExport\\] no ramp configured","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"convex/broadcast/rampRunner.ts","lineNumber":718,"sourceCode":" * `assignAndExportWave` returns successfully. Lets `recoverFromPartialFailure`\n * recover the (segmentId, assigned, waveLabel) without operator-supplied\n * metadata if the action dies between this point and a successful\n * `_recordWaveSent`.\n *\n * Lease-validating: throws if the lease has changed (operator\n * `forceReleaseLease` mid-flight, or a different run claimed). The throw\n * bubbles to Convex auto-Sentry; the runner stops without advancing.\n */\nexport const _recordPendingExport = internalMutation({\n  args: {\n    runId: v.string(),\n    waveLabel: v.string(),\n    segmentId: v.string(),\n    assigned: v.number(),\n  },\n  handler: async (ctx, args) => {\n    const row = await loadConfig(ctx);\n    if (!row) throw new Error(\"[_recordPendingExport] no ramp configured\");\n    if (row.pendingRunId !== args.runId) {\n      throw new Error(\n        `[_recordPendingExport] lease lost: expected runId=${args.runId}, found ${row.pendingRunId ?? \"<cleared>\"}. Refusing to persist export progress — operator/another run owns the state.`,\n      );\n    }\n    await ctx.db.patch(row._id, {\n      pendingWaveLabel: args.waveLabel,\n      pendingSegmentId: args.segmentId,\n      pendingAssigned: args.assigned,\n      pendingExportAt: Date.now(),\n    });\n    return { ok: true as const };\n  },\n});\n\n/**\n * Persist post-`createProLaunchBroadcast` progress. Called by the runner\n * AFTER `createProLaunchBroadcast` returns successfully. Lets","sourceCodeStart":700,"sourceCodeEnd":736,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/broadcast/rampRunner.ts#L700-L736","documentation":"_recordPendingExport is an internal mutation called by the runner to persist export-side progress (pendingWaveLabel/pendingSegmentId/pendingAssigned) after assignAndExportWave succeeds but before the broadcast side effect. It loads the config via loadConfig and throws if no row exists — the runner cannot persist progress against a config that was aborted mid-flight.","triggerScenarios":"The runner reached the export-record step but abortRamp deleted the config row between the run starting and this mutation; or the run is executing in an unseeded deployment.","commonSituations":"Operator aborted the ramp while a wave action was in flight; a parallel run/recovery cleared the config; deployment mismatch.","solutions":["Do not retry — the config is gone by operator intent. Let the run fail and surface to Sentry, then decide whether to re-seed.","If the abort was accidental, re-seed via initRamp and re-trigger the wave fresh (the export did not get recorded).","Ensure only one operator path (abortRamp vs. live runner) mutates the config at a time."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Runner-side: re-check config existence right before persisting export progress\nconst row = await ctx.db.query('broadcastRampConfig').withIndex('by_key', (q) => q.eq('key', 'current')).first();\nif (!row) {\n  // abort this run cleanly; operator aborted the ramp mid-flight\n  return { aborted: 'config-gone' };\n}","typeGuard":null,"tryCatchPattern":"try {\n  await ctx.runMutation(internal.broadcast.rampRunner._recordPendingExport, { runId, waveLabel, segmentId, assigned });\n} catch (e) {\n  // config gone or lease lost — do NOT retry; surface and let ops reconcile\n  console.error('[runner] _recordPendingExport failed:', (e as Error).message);\n  throw e;\n}","preventionTips":["Serialize operator aborts against live runs to avoid deleting the config mid-flight.","Runner should treat this as terminal, not retriable.","Confirm the deployment is seeded before triggering waves."],"tags":["convex","broadcast-ramp","state-missing","lease","runner-internal"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}