{"record":{"id":"0e779701303a6141","repo":"mifi/lossless-cut","slug":"failed-to-find-next-keyframe","errorCode":null,"errorMessage":"Failed to find next keyframe","messagePattern":"Failed to find next keyframe","errorType":"exception","errorClass":"UserFacingError","httpStatus":null,"severity":"error","filePath":"src/renderer/src/ffmpeg.ts","lineNumber":157,"sourceCode":"\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  };\n\n  index = findReverseIndex(frames, (f) => f.time <= cutTime + sigma);\n  if (index === -1) throw new UserFacingError(i18n.t('Failed to find any prev frame'));\n  if (index === 0) throw new UserFacingError(i18n.t('We are on the first frame'));","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/mifi/lossless-cut/blob/3b9a59c288bf6e11076b583c932cfa48ddab3b02/src/renderer/src/ffmpeg.ts#L139-L175","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Widen the frame/keyframe probe window so it extends past cutTime before calling getSafeCutTime.","Choose prevMode instead of nextMode if the cut is near the end of the file.","If cutting at end-of-file, treat the missing next keyframe as 'no adjustment needed' rather than an error.","Re-mux/re-encode the source with denser keyframes (-g) if cuts must land near the end."],"exampleFix":"// before\nconst t = getSafeCutTime(frames, cutTime, true);\n\n// after\nconst hasKeyframeAfter = frames.some((f) => f.keyframe && f.time >= cutTime - 0.01);\nconst t = hasKeyframeAfter ? getSafeCutTime(frames, cutTime, true) : undefined;","handlingStrategy":"validation","validationCode":"const sigma = 0.01;\nconst hasKeyframeAfter = frames.some((f) => f.keyframe && f.time >= cutTime - sigma);\nif (nextMode && !hasKeyframeAfter) {\n  // no forward keyframe: use prevMode or skip adjustment\n  return undefined;\n}","typeGuard":null,"tryCatchPattern":"try {\n  return getSafeCutTime(frames, cutTime, true);\n} catch (err) {\n  if (err instanceof UserFacingError && /next keyframe/.test(err.message)) {\n    return undefined; // no adjustment possible\n  }\n  throw err;\n}","preventionTips":["Probe frames over a window that extends beyond cutTime.","Switch to prevMode for cuts near the end of the file.","Treat 'no next keyframe' at end-of-file as a no-op rather than an error."],"tags":["ffmpeg","keyframe","smart-cut","frames","next-mode"],"backgroundTag":null,"analyzedSha":"3b9a59c288bf6e11076b583c932cfa48ddab3b02","analyzedAt":"2026-08-12T20:54:25.651Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}