mifi/lossless-cut · error · UserFacingError

Less than 2 frames found

Error message

Less than 2 frames found

What it means

Thrown by getSafeCutTime() when the frames array passed in has fewer than 2 elements. The function computes a safe keyframe-aligned cut by inspecting neighbouring frames, which is meaningless without at least two frames to reason about ordering. This is a defensive precondition check before any index arithmetic.

Source

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

  if (!nearByKeyframe) {
    keyframes = await readKeyframesAroundTime({ filePath, streamIndex, aroundTime: time, window: 60 });
    nearByKeyframe = findKeyframe(keyframes, time, mode);
  }

  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;
  };

View on GitHub (pinned to 3b9a59c288)

Solutions

  1. Ensure the frames array is fully populated (await the keyframe/frame probe) before calling getSafeCutTime.
  2. Skip smart-cut / safe-cut for clips with fewer than 2 frames and fall back to a plain lossless cut.
  3. Diagnose the source media with `ffprobe -show_frames` to confirm it actually contains multiple frames.
  4. Guard the caller: if (frames.length < 2) return cutTime unchanged instead of invoking the function.

Example fix

// before
const safe = getSafeCutTime(frames, cutTime, nextMode);

// after
const safe = frames.length >= 2 ? getSafeCutTime(frames, cutTime, nextMode) : undefined;
Defensive patterns

Strategy: validation

Validate before calling

// Ensure at least 2 frames before calling getSafeCutTime
if (!Array.isArray(frames) || frames.length < 2) {
  throw new Error(`Need >= 2 frames for safe cut, got ${frames?.length ?? 0}`);
}

Type guard

const hasMinFrames = (frames: unknown): frames is Frame[] => Array.isArray(frames) && frames.length >= 2;

Prevention

When it happens

Trigger: Calling getSafeCutTime(frames, cutTime, nextMode) with a frames array of length 0 or 1; passing the result of a keyframe probe that returned only the single current frame; a video stream whose ffprobe frame list was truncated or empty due to a read error.

Common situations: Very short source clips (1 frame); corrupted/damaged media where ffprobe could only decode one frame; calling the function on a still-image or single-frame stream; a race where the frames array was populated asynchronously and read before completion.

Related errors


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