koala73/worldmonitor · error · Error
[_recordPendingBroadcast] no ramp configured
Error message
[_recordPendingBroadcast] no ramp configured
What it means
_recordPendingBroadcast is the internal mutation that persists pendingBroadcastId/pendingBroadcastAt after createProLaunchBroadcast returns a broadcast id. It loads the config via loadConfig and throws if no row exists, since recording broadcast progress against an aborted/non-existent ramp would corrupt recovery state.
Source
Thrown at convex/broadcast/rampRunner.ts:750
});
/**
* Persist post-`createProLaunchBroadcast` progress. Called by the runner
* AFTER `createProLaunchBroadcast` returns successfully. Lets
* `recoverFromPartialFailure` recover the broadcastId without
* operator-supplied metadata if the action dies between this point and a
* successful `_recordWaveSent`.
*
* Lease-validating: same semantics as `_recordPendingExport`.
*/
export const _recordPendingBroadcast = internalMutation({
args: {
runId: v.string(),
broadcastId: v.string(),
},
handler: async (ctx, args) => {
const row = await loadConfig(ctx);
if (!row) throw new Error("[_recordPendingBroadcast] no ramp configured");
if (row.pendingRunId !== args.runId) {
throw new Error(
`[_recordPendingBroadcast] lease lost: expected runId=${args.runId}, found ${row.pendingRunId ?? "<cleared>"}. Refusing to persist broadcast progress — operator/another run owns the state.`,
);
}
await ctx.db.patch(row._id, {
pendingBroadcastId: args.broadcastId,
pendingBroadcastAt: Date.now(),
});
return { ok: true as const };
},
});
/**
* Internal mutation that the action calls to atomically advance the tier +
* record a successful wave-send. Validates that the lease still belongs to
* this runId AND clears all pending-progress markers.
*/View on GitHub (pinned to ffec79ac33)
Solutions
- Treat as non-retriable: the config is gone by intent. Let it surface to Sentry; decide whether to re-seed.
- If accidental abort, re-seed via initRamp and re-run the wave (note a Resend broadcast may already have been created — dedupe manually).
- Serialize operator abort against live runs.
Defensive patterns
Strategy: try-catch
Validate before calling
// Runner-side: re-check config existence before recording broadcast progress
const row = await ctx.db.query('broadcastRampConfig').withIndex('by_key', (q) => q.eq('key', 'current')).first();
if (!row) {
return { aborted: 'config-gone' }; // operator aborted mid-flight; do not retry
} Try / catch
try {
await ctx.runMutation(internal.broadcast.rampRunner._recordPendingBroadcast, { runId, broadcastId });
} catch (e) {
console.error('[runner] _recordPendingBroadcast failed:', (e as Error).message);
throw e; // surface; reconcile manually — a broadcast may already exist in Resend
} Prevention
- Avoid aborting the ramp during in-flight broadcast creation.
- If this fires, check Resend for the created broadcast before re-running to avoid duplicates.
- Serialize operator interventions against live runs.
When it happens
Trigger: The runner reached the broadcast-record step but abortRamp deleted the config between run start and this mutation; or the run executes in an unseeded deployment.
Common situations: Operator aborted the ramp during an in-flight broadcast creation; deployment mismatch; parallel recovery cleared the config.
Related errors
- [_recordPendingExport] no ramp configured
- [_recordWaveSent] no ramp configured
- [forceReleaseLease] no ramp configured
- [_recordPendingExport] lease lost: expected runId=${args.run
- [_recordPendingBroadcast] lease lost: expected runId=${args.
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/a738ce2e5fbf99c2.
Report an issue: GitHub.