koala73/worldmonitor · error · Error
[resumeRamp] kill-gate is tripped; clearKillGate first after
Error message
[resumeRamp] kill-gate is tripped; clearKillGate first after investigating.
What it means
resumeRamp refuses to flip active=true when broadcastRampConfig.killGateTripped is true. The kill-gate is tripped by the runner when a prior wave's bounce or complaint rate exceeded the configured threshold (DEFAULT_BOUNCE_KILL_THRESHOLD / DEFAULT_COMPLAINT_KILL_THRESHOLD). Resuming would re-enable sending while sender reputation is at risk, so the operator must explicitly clear the gate after investigating.
Source
Thrown at convex/broadcast/rampRunner.ts:235
});
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 };
},
});
export const clearKillGate = internalMutation({
args: { reason: v.string() },
handler: async (ctx, { reason }) => {
const row = await loadConfig(ctx);
if (!row) throw new Error("[clearKillGate] no ramp configured");
if (!row.killGateTripped) {
return { ok: true, noop: true };
}
await ctx.db.patch(row._id, {
killGateTripped: false,View on GitHub (pinned to ffec79ac33)
Solutions
- Investigate the trip: check getRampStatus().killGateReason and the last wave's Resend bounce/complaint stats.
- Resolve the list/content issue (suppress hard bounces, fix content causing complaints), then call clearKillGate({reason}) with a documented justification.
- Then call resumeRamp to re-enable the cron.
Example fix
// before
await ctx.runMutation(internal.broadcast.rampRunner.resumeRamp, {});
// after
const status = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});
if (status.configured && status.killGateTripped) {
throw new Error(`Kill-gate tripped: ${status.killGateReason}. Investigate, then clearKillGate.`);
}
await ctx.runMutation(internal.broadcast.rampRunner.resumeRamp, {}); Defensive patterns
Strategy: validation
Validate before calling
// Preflight before resumeRamp — block if kill-gate is tripped
const status = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});
if (status.configured && status.killGateTripped) {
throw new Error(`Kill-gate tripped (${status.killGateReason}). Investigate, then clearKillGate before resumeRamp.`);
} Type guard
function killGateIsTripped(s: { configured: boolean; killGateTripped?: boolean }): boolean {
return s.configured === true && s.killGateTripped === true;
} Prevention
- Always inspect killGateReason and the last wave's Resend bounce/complaint stats before clearing.
- Never auto-resume: require an explicit operator decision after a kill-gate trip.
- Tune bounceKillThreshold/complaintKillThreshold deliberately; defaults exist for sender reputation protection.
When it happens
Trigger: Calling resumeRamp after the daily runner recorded a kill-gate trip; calling it after manually setting killGateTripped=true; resuming while the last wave's Resend stats still show out-of-threshold bounce/complaint.
Common situations: A wave hit a hard-bounce spike (stale list) or complaint spike and the runner auto-tripped the gate; the operator wants to resume but has not yet diagnosed root cause.
Related errors
- [clearKillGate] no ramp configured
- [clearPartialFailure] refused: pending progress markers pres
- [initRamp] waveLabelOffset=${offset} signals resumption afte
- [pauseRamp] no ramp configured
- [resumeRamp] no ramp configured
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/8811b067a4510a9b.
Report an issue: GitHub.