koala73/worldmonitor · error · Error

[pauseRamp] no ramp configured

Error message

[pauseRamp] no ramp configured

What it means

pauseRamp is a Convex internalMutation that sets broadcastRampConfig.active=false. It reads the singleton config row via loadConfig(ctx), which queries the broadcastRampConfig table by key='current'. If no row exists the handler throws rather than no-op'ing, so an operator cannot silently 'pause' a ramp that was never seeded and believe it took effect.

Source

Thrown at convex/broadcast/rampRunner.ts:223

      lastWaveBroadcastId: args.seedLastWaveBroadcastId,
      lastWaveSentAt: args.seedLastWaveSentAt,
      lastWaveLabel: args.seedLastWaveLabel,
      lastWaveSegmentId: args.seedLastWaveSegmentId,
      lastWaveAssigned: args.seedLastWaveAssigned,
      // Default-FALSE on insert when arg omitted. Per the design doc:
      // explicit, never silently true — existing-ramp byte-identical
      // behavior is the default; operators opt in for wave-8+.
      excludeNonEnglish: args.excludeNonEnglish ?? false,
    });
    return { ok: true };
  },
});

export const pauseRamp = internalMutation({
  args: {},
  handler: async (ctx) => {
    const row = await loadConfig(ctx);
    if (!row) throw new Error("[pauseRamp] no ramp configured");
    await ctx.db.patch(row._id, { active: false });
    return { ok: true, prevActive: row.active };
  },
});

export const resumeRamp = internalMutation({
  args: {},
  handler: async (ctx) => {
    const row = await loadConfig(ctx);
    if (!row) throw new Error("[resumeRamp] no ramp configured");
    if (row.killGateTripped) {
      throw new Error(
        "[resumeRamp] kill-gate is tripped; clearKillGate first after investigating.",
      );
    }
    await ctx.db.patch(row._id, { active: true });
    return { ok: true };
  },

View on GitHub (pinned to ffec79ac33)

Solutions

  1. Preflight with getRampStatus; if it returns {configured:false}, run initRamp first to seed the config row.
  2. If the ramp was aborted, re-run initRamp with the original rampCurve and seedLastWave* fields to recreate the row, then pauseRamp.
  3. Confirm the calling client/cron targets the correct Convex deployment (CONVEX_DEPLOYMENT / VITE_CONVEX_URL) that holds the seeded config.

Example fix

// before
await ctx.runMutation(internal.broadcast.rampRunner.pauseRamp, {});

// after
const status = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});
if (!status.configured) throw new Error('Cannot pause: ramp not configured. Run initRamp first.');
await ctx.runMutation(internal.broadcast.rampRunner.pauseRamp, {});
Defensive patterns

Strategy: validation

Validate before calling

// Preflight before pauseRamp
const status = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});
if (!status.configured) {
  throw new Error('Ramp not configured — run initRamp before pauseRamp.');
}

Type guard

type RampStatus =
  | { configured: false }
  | { configured: true; active: boolean; killGateTripped: boolean; currentTier: number; rampCurve: number[] };

function isConfigured(s: RampStatus): s is Extract<RampStatus, { configured: true }> {
  return s.configured === true;
}

Prevention

When it happens

Trigger: Calling the pauseRamp mutation before initRamp has ever run; calling it after abortRamp (which deletes the row); calling it from a cron/automation in a fresh Convex deployment or preview that was never seeded.

Common situations: New or staging Convex project that was never initialized; CI pointing at the wrong CONVEX_DEPLOYMENT; an operator ran abortRamp during incident triage and a still-scheduled job fires pauseRamp afterward.

Related errors


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/95319139fd6ad688. Report an issue: GitHub.