koala73/worldmonitor · error · Error
[_recordWaveSent] no ramp configured
Error message
[_recordWaveSent] no ramp configured
What it means
_recordWaveSent is the terminal success commit: it advances currentTier and records lastWave* metadata after a wave is sent. It loads the config via loadConfig and throws if no row exists, because committing a wave against an aborted ramp would be inconsistent.
Source
Thrown at convex/broadcast/rampRunner.ts:782
/**
* 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.
*/
export const _recordWaveSent = internalMutation({
args: {
runId: v.string(),
expectedCurrentTier: v.number(),
newTier: v.number(),
waveLabel: v.string(),
broadcastId: v.string(),
segmentId: v.string(),
assigned: v.number(),
sentAt: v.number(),
},
handler: async (ctx, args) => {
const row = await loadConfig(ctx);
if (!row) throw new Error("[_recordWaveSent] no ramp configured");
if (row.currentTier !== args.expectedCurrentTier) {
throw new Error(
`[_recordWaveSent] tier moved underneath us: expected ${args.expectedCurrentTier}, found ${row.currentTier}. Refusing to overwrite.`,
);
}
if (row.pendingRunId !== args.runId) {
// The lease changed under us — operator force-released it, or
// recoverFromPartialFailure cleared it. We must NOT advance the tier;
// bubble to Convex auto-Sentry so ops can investigate.
throw new Error(
`[_recordWaveSent] lease lost: expected runId=${args.runId}, found ${row.pendingRunId ?? "<cleared>"}. Refusing to advance tier — investigate what cleared the lease.`,
);
}
await ctx.db.patch(row._id, {
currentTier: args.newTier,
lastWaveLabel: args.waveLabel,
lastWaveBroadcastId: args.broadcastId,
lastWaveSegmentId: args.segmentId,View on GitHub (pinned to ffec79ac33)
Solutions
- Do not retry — the config is gone by intent. Surface to Sentry; verify whether the Resend broadcast actually sent.
- If accidental abort, re-seed via initRamp with the correct currentTier/seed so the next wave is the right one (avoid double-sending the just-sent wave).
- Serialize operator aborts against live runs.
Defensive patterns
Strategy: try-catch
Validate before calling
// Runner-side: re-check config existence before the terminal commit
const row = await ctx.db.query('broadcastRampConfig').withIndex('by_key', (q) => q.eq('key', 'current')).first();
if (!row) {
return { aborted: 'config-gone' }; // a broadcast may have sent — reconcile manually
} Try / catch
try {
await ctx.runMutation(internal.broadcast.rampRunner._recordWaveSent, { runId, expectedCurrentTier, newTier, ... });
} catch (e) {
console.error('[runner] _recordWaveSent failed:', (e as Error).message);
throw e; // ops must verify whether the Resend broadcast actually sent
} Prevention
- Never abort the ramp during an in-flight send.
- If this fires, check Resend delivery state for the broadcast before re-seeding.
- Re-seed with the correct currentTier to avoid re-sending the just-sent wave.
When it happens
Trigger: The runner reached the final commit step but abortRamp deleted the config between run start and _recordWaveSent; or the run executes in an unseeded deployment.
Common situations: Operator aborted during an in-flight send; deployment mismatch; parallel recovery cleared the config.
Related errors
- [_recordPendingExport] no ramp configured
- [_recordPendingBroadcast] 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/f0bfb00fa3d740ea.
Report an issue: GitHub.