{"record":{"id":"3ac6580eaf8f3f52","repo":"mifi/lossless-cut","slug":"less-than-2-frames-found","errorCode":null,"errorMessage":"Less than 2 frames found","messagePattern":"Less than 2 frames found","errorType":"exception","errorClass":"UserFacingError","httpStatus":null,"severity":"error","filePath":"src/renderer/src/ffmpeg.ts","lineNumber":153,"sourceCode":"  if (!nearByKeyframe) {\n    keyframes = await readKeyframesAroundTime({ filePath, streamIndex, aroundTime: time, window: 60 });\n    nearByKeyframe = findKeyframe(keyframes, time, mode);\n  }\n\n  if (!nearByKeyframe) return undefined;\n  return nearByKeyframe.time;\n}\n\n// todo this is not in use\n// https://stackoverflow.com/questions/14005110/how-to-split-a-video-using-ffmpeg-so-that-each-chunk-starts-with-a-key-frame\n// http://kicherer.org/joomla/index.php/de/blog/42-avcut-frame-accurate-video-cutting-with-only-small-quality-loss\nexport function getSafeCutTime(frames: Frame[], cutTime: number, nextMode: boolean) {\n  const sigma = 0.01;\n  const isCloseTo = (time1: number, time2: number) => Math.abs(time1 - time2) < sigma;\n\n  let index: number;\n\n  if (frames.length < 2) throw new UserFacingError(i18n.t('Less than 2 frames found'));\n\n  if (nextMode) {\n    index = frames.findIndex((f) => f.keyframe && f.time >= cutTime - sigma);\n    if (index === -1) throw new UserFacingError(i18n.t('Failed to find next keyframe'));\n    if (index >= frames.length - 1) throw new UserFacingError(i18n.t('We are on the last frame'));\n    const { time } = frames[index]!;\n    if (isCloseTo(time, cutTime)) {\n      return undefined; // Already on keyframe, no need to modify cut time\n    }\n    return time;\n  }\n\n  const findReverseIndex = <T>(arr: T[], cb: (value: T, i: number, obj: T[]) => unknown) => {\n    // eslint-disable-next-line unicorn/no-array-callback-reference\n    const ret = [...arr].reverse().findIndex(cb);\n    if (ret === -1) return -1;\n    return arr.length - 1 - ret;\n  };","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/mifi/lossless-cut/blob/3b9a59c288bf6e11076b583c932cfa48ddab3b02/src/renderer/src/ffmpeg.ts#L135-L171","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the frames array is fully populated (await the keyframe/frame probe) before calling getSafeCutTime.","Skip smart-cut / safe-cut for clips with fewer than 2 frames and fall back to a plain lossless cut.","Diagnose the source media with `ffprobe -show_frames` to confirm it actually contains multiple frames.","Guard the caller: if (frames.length < 2) return cutTime unchanged instead of invoking the function."],"exampleFix":"// before\nconst safe = getSafeCutTime(frames, cutTime, nextMode);\n\n// after\nconst safe = frames.length >= 2 ? getSafeCutTime(frames, cutTime, nextMode) : undefined;","handlingStrategy":"validation","validationCode":"// Ensure at least 2 frames before calling getSafeCutTime\nif (!Array.isArray(frames) || frames.length < 2) {\n  throw new Error(`Need >= 2 frames for safe cut, got ${frames?.length ?? 0}`);\n}","typeGuard":"const hasMinFrames = (frames: unknown): frames is Frame[] => Array.isArray(frames) && frames.length >= 2;","tryCatchPattern":null,"preventionTips":["Await the full frame/keyframe probe before invoking getSafeCutTime.","Skip smart/safe cut for very short clips and fall back to a plain lossless cut.","Treat a <2-frame stream as a degenerate/still source rather than a cuttable video."],"tags":["ffmpeg","keyframe","smart-cut","frames","validation"],"backgroundTag":null,"analyzedSha":"3b9a59c288bf6e11076b583c932cfa48ddab3b02","analyzedAt":"2026-08-12T20:54:25.651Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}