mifi/lossless-cut · error · UserFacingError

Smart cut is not possible when FPS is unknown

Error message

Smart cut is not possible when FPS is unknown

What it means

Thrown during smart-cut export when a segment is flagged as needing a smart (re-encode) cut but the detected video FPS is falsy. Smart cut must convert between frame numbers and timestamps and re-encode the partial GOP, both of which require a known frame rate; without it the cut would be frame-inaccurate. The guard fires after needsSmartCut() reports segmentNeedsSmartCut and before the encode begins.

Source

Thrown at src/renderer/src/hooks/useFfmpegOperations.ts:655

        invariant(sourceCodecParams.videoBitrate != null);
        invariant(sourceCodecParams.videoTimebase != null);
        invariant(filePath != null);
        invariant(outFormat != null);
        await cutEncodeSmartPart({ cutFrom, cutTo, outPath, outFormat, videoCodec, videoBitrate: encCustomBitrate != null ? encCustomBitrate * 1000 : sourceCodecParams.videoBitrate, videoStreamIndex: videoStream.index, videoTimebase: sourceCodecParams.videoTimebase, allFilesMeta, copyFileStreams: copyFileStreamsFiltered, ffmpegExperimental, hasBFrames: sourceCodecParams.videoStream.has_b_frames });
      }

      const cutEncodeWholePart = async () => {
        await cutEncodeSmartPartWrapper({ cutFrom: desiredCutFrom, cutTo, outPath: finalOutPath });
        return { path: finalOutPath, created: true };
      };

      if (lossyMode) {
        console.log('Lossy mode: cutting/encoding the whole segment', { desiredCutFrom, cutTo });
        return cutEncodeWholePart();
      }

      const { losslessCutFrom, segmentNeedsSmartCut } = await needsSmartCut({ path: filePath, desiredCutFrom, videoStream });
      if (segmentNeedsSmartCut && !detectedFps) throw new UserFacingError(i18n.t('Smart cut is not possible when FPS is unknown'));
      console.log('Smart cut on video stream', videoStream.index);

      // If we are cutting within two keyframes, just encode the whole part and return that
      // See https://github.com/mifi/lossless-cut/pull/1267#issuecomment-1236381740
      if (segmentNeedsSmartCut && losslessCutFrom > cutTo) {
        console.log('Segment is between two keyframes, cutting/encoding the whole segment', { desiredCutFrom, losslessCutFrom, cutTo });
        return cutEncodeWholePart();
      }

      invariant(outFormat != null);

      const ext = getOutFileExtension({ isCustomFormatSelected: true, outFormat, filePath });

      if (segmentNeedsSmartCut) {
        console.log('Cutting/encoding lossless part', { from: losslessCutFrom, to: cutTo });
      }

      const losslessPartOutPath = segmentNeedsSmartCut

View on GitHub (pinned to 3b9a59c288)

Solutions

  1. Disable smart cut (normal/keyframe cut) for streams whose FPS cannot be detected.
  2. Detect and supply the FPS explicitly: parse avg_frame_rate/r_frame_rate and fall back to a manual value if absent.
  3. Re-mux/re-encode the source with a fixed frame rate so ffprobe reports it (ffmpeg -r).
  4. Validate detectedFps is a finite positive number before entering the smart-cut code path.

Example fix

// before
if (segmentNeedsSmartCut && !detectedFps) throw new UserFacingError(...);

// after
const fps = detectedFps || fallbackFpsFromProbe(videoStream);
if (segmentNeedsSmartCut && !fps) {
  // degrade gracefully to lossless keyframe cut instead of throwing
  return cutEncodeWholePart();
}
Defensive patterns

Strategy: validation

Validate before calling

// Detect FPS robustly from ffprobe stream fields
export function detectFps(stream: { avg_frame_rate?: string; r_frame_rate?: string }): number | undefined {
  const parse = (s?: string) => {
    if (!s) return undefined;
    const [n, d] = s.split('/').map(Number);
    if (!Number.isFinite(n) || !Number.isFinite(d) || d === 0) return undefined;
    const fps = n / d;
    return Number.isFinite(fps) && fps > 0 ? fps : undefined;
  };
  return parse(stream.avg_frame_rate) ?? parse(stream.r_frame_rate);
}
const detectedFps = detectFps(videoStream);
if (!detectedFps && wantSmartCut) throw new Error('Cannot smart cut: FPS unknown');

Type guard

const hasUsableFps = (fps: number | undefined): fps is number => typeof fps === 'number' && Number.isFinite(fps) && fps > 0;

Try / catch

try {
  await smartCutExport(...);
} catch (err) {
  if (err instanceof UserFacingError && /FPS is unknown/.test(err.message)) {
    // fall back to lossless keyframe cut
    await losslessCutExport(...);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Exporting with smart cut enabled on a video stream whose ffprobe reported no avg_frame_rate / r_frame_rate (variable frame rate, or a container that omits the field); a stream whose FPS parsed to 0/NaN; enabling smart cut on an image sequence or still stream.

Common situations: VFR (variable frame rate) screen recordings or mobile clips; streams with FPS as '0/0' in ffprobe; damaged headers; mixing a no-FPS stream into a smart-cut job; force-enabling smart cut on a stream type it does not support.

Related errors


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