mifi/lossless-cut · error · UserFacingError

Failed to find next keyframe

Error message

Failed to find next keyframe

What it means

Thrown by getSafeCutTime() in nextMode when findIndex fails to locate any keyframe whose time is >= cutTime - sigma (0.01s). In nextMode the function needs the next keyframe at or after the desired cut to align the cut forward, so an absent forward keyframe is fatal. It means there is no keyframe from the cut point to the end of the known frame list.

Source

Thrown at src/renderer/src/ffmpeg.ts:157

  if (!nearByKeyframe) return undefined;
  return nearByKeyframe.time;
}

// todo this is not in use
// https://stackoverflow.com/questions/14005110/how-to-split-a-video-using-ffmpeg-so-that-each-chunk-starts-with-a-key-frame
// http://kicherer.org/joomla/index.php/de/blog/42-avcut-frame-accurate-video-cutting-with-only-small-quality-loss
export function getSafeCutTime(frames: Frame[], cutTime: number, nextMode: boolean) {
  const sigma = 0.01;
  const isCloseTo = (time1: number, time2: number) => Math.abs(time1 - time2) < sigma;

  let index: number;

  if (frames.length < 2) throw new UserFacingError(i18n.t('Less than 2 frames found'));

  if (nextMode) {
    index = frames.findIndex((f) => f.keyframe && f.time >= cutTime - sigma);
    if (index === -1) throw new UserFacingError(i18n.t('Failed to find next keyframe'));
    if (index >= frames.length - 1) throw new UserFacingError(i18n.t('We are on the last frame'));
    const { time } = frames[index]!;
    if (isCloseTo(time, cutTime)) {
      return undefined; // Already on keyframe, no need to modify cut time
    }
    return time;
  }

  const findReverseIndex = <T>(arr: T[], cb: (value: T, i: number, obj: T[]) => unknown) => {
    // eslint-disable-next-line unicorn/no-array-callback-reference
    const ret = [...arr].reverse().findIndex(cb);
    if (ret === -1) return -1;
    return arr.length - 1 - ret;
  };

  index = findReverseIndex(frames, (f) => f.time <= cutTime + sigma);
  if (index === -1) throw new UserFacingError(i18n.t('Failed to find any prev frame'));
  if (index === 0) throw new UserFacingError(i18n.t('We are on the first frame'));

View on GitHub (pinned to 3b9a59c288)

Solutions

  1. Widen the frame/keyframe probe window so it extends past cutTime before calling getSafeCutTime.
  2. Choose prevMode instead of nextMode if the cut is near the end of the file.
  3. If cutting at end-of-file, treat the missing next keyframe as 'no adjustment needed' rather than an error.
  4. Re-mux/re-encode the source with denser keyframes (-g) if cuts must land near the end.

Example fix

// before
const t = getSafeCutTime(frames, cutTime, true);

// after
const hasKeyframeAfter = frames.some((f) => f.keyframe && f.time >= cutTime - 0.01);
const t = hasKeyframeAfter ? getSafeCutTime(frames, cutTime, true) : undefined;
Defensive patterns

Strategy: validation

Validate before calling

const sigma = 0.01;
const hasKeyframeAfter = frames.some((f) => f.keyframe && f.time >= cutTime - sigma);
if (nextMode && !hasKeyframeAfter) {
  // no forward keyframe: use prevMode or skip adjustment
  return undefined;
}

Try / catch

try {
  return getSafeCutTime(frames, cutTime, true);
} catch (err) {
  if (err instanceof UserFacingError && /next keyframe/.test(err.message)) {
    return undefined; // no adjustment possible
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling getSafeCutTime(frames, cutTime, true) where cutTime is at or beyond the last keyframe in the supplied frames list; the frames array only covers a window before the cut; a video where the only keyframes precede the cut point (e.g. cut near end of a stream with sparse keyframes).

Common situations: Cutting very close to the end of a file; passing a truncated frame list that does not span the cut; GOP-structured video with long keyframe intervals (e.g. 10s GOP) and a cut placed right after the final keyframe; re-encoding output whose keyframes were not yet detected.

Related errors


AI-assisted analysis of mifi/lossless-cut@3b9a59c288 (2026-08-12). Data as JSON: /api/errors/0e779701303a6141. Report an issue: GitHub.