koala73/worldmonitor · error · Error
[recoverFromPartialFailure:manual-finished] currentTier=${ro
Error message
[recoverFromPartialFailure:manual-finished] currentTier=${row.currentTier} would advance past rampCurve.length=${row.rampCurve.length}. Curve is complete; nothing to recover. What it means
In the 'manual-finished' recovery branch, recoverFromPartialFailure computes nextTier = currentTier + 1 to advance the ramp. If nextTier is already >= rampCurve.length, the ramp curve is fully consumed and there is no further tier to advance to — recovering would write an out-of-bounds tier. The handler throws rather than silently no-op'ing or corrupting the pointer.
Source
Thrown at convex/broadcast/rampRunner.ts:399
pendingRunId: undefined,
pendingRunStartedAt: undefined,
pendingWaveLabel: undefined,
pendingSegmentId: undefined,
pendingAssigned: undefined,
pendingExportAt: undefined,
pendingBroadcastId: undefined,
pendingBroadcastAt: undefined,
} as const;
if (args.recovery === "manual-finished") {
// Resolve each field: operator-supplied wins, persisted pending* is the
// fallback. sentAt is operator-only (no fallback).
const broadcastId = args.broadcastId ?? row.pendingBroadcastId;
const segmentId = args.segmentId ?? row.pendingSegmentId;
const assigned = args.assigned ?? row.pendingAssigned;
const nextTier = row.currentTier + 1;
if (nextTier >= row.rampCurve.length) {
throw new Error(
`[recoverFromPartialFailure:manual-finished] currentTier=${row.currentTier} would advance past rampCurve.length=${row.rampCurve.length}. Curve is complete; nothing to recover.`,
);
}
const waveLabel =
args.waveLabel ??
row.pendingWaveLabel ??
`${row.waveLabelPrefix}-${nextTier + row.waveLabelOffset}`;
const missing: string[] = [];
if (!broadcastId) missing.push("broadcastId");
if (!segmentId) missing.push("segmentId");
if (assigned === undefined) missing.push("assigned");
if (args.sentAt === undefined) missing.push("sentAt");
if (missing.length > 0) {
throw new Error(
`[recoverFromPartialFailure:manual-finished] missing required field(s): ${missing.join(", ")}. ` +
`Operator must supply (or rely on persisted pending* state from a prior runner). Persisted state: ` +
`pendingBroadcastId=${row.pendingBroadcastId ?? "-"}, pendingSegmentId=${row.pendingSegmentId ?? "-"}, ` +View on GitHub (pinned to ffec79ac33)
Solutions
- Treat the ramp as complete: if the wave genuinely sent, record it directly (lastWave* fields) rather than advancing the tier, or run abortRamp.
- Verify currentTier against rampCurve.length via getRampStatus before calling recovery.
- If more waves are genuinely needed, abortRamp and initRamp with an extended rampCurve + correct waveLabelOffset/seed fields.
Example fix
// before
await ctx.runMutation(internal.broadcast.rampRunner.recoverFromPartialFailure, { recovery: 'manual-finished', reason });
// after
const s = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});
if (s.configured && s.currentTier + 1 >= s.rampCurve.length) {
throw new Error('Ramp curve complete — nothing to advance. Record manually or abortRamp.');
}
await ctx.runMutation(internal.broadcast.rampRunner.recoverFromPartialFailure, { recovery: 'manual-finished', reason }); Defensive patterns
Strategy: validation
Validate before calling
// Before manual-finished recovery, ensure the curve has room to advance
const s = await ctx.runQuery(internal.broadcast.rampRunner.getRampStatus, {});
if (s.configured && s.currentTier + 1 >= s.rampCurve.length) {
throw new Error('Ramp curve complete — nothing to recover/advance. Record manually or abortRamp.');
} Type guard
function curveHasRoom(s: { configured: boolean; currentTier?: number; rampCurve?: number[] }): boolean {
return s.configured === true && typeof s.currentTier === 'number' && Array.isArray(s.rampCurve) && s.currentTier + 1 < s.rampCurve.length;
} Prevention
- Check currentTier vs rampCurve.length before manual-finished recovery.
- If the final wave sent, record it directly rather than advancing past the curve.
- Extend rampCurve via abort+init if more waves are genuinely needed.
When it happens
Trigger: Calling recoverFromPartialFailure({recovery:'manual-finished'}) when the last partial failure occurred on the final tier of the rampCurve and currentTier+1 would exceed the curve length.
Common situations: The final wave partially failed; the operator manually confirmed it sent and tries to mark it finished, but the curve has no room to advance; the ramp already reached completion elsewhere.
Related errors
- [recoverFromPartialFailure:manual-finished] missing required
- [pauseRamp] no ramp configured
- [resumeRamp] no ramp configured
- [clearPartialFailure] no ramp configured
- [clearPartialFailure] refused: pending progress markers pres
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/9da987c84741c550.
Report an issue: GitHub.