mifi/lossless-cut · error · UserFacingError

Invalid duration

Error message

Invalid duration

What it means

Thrown in App.tsx during the 'convert to supported format' recovery path when the main file's ffprobe duration fails isDurationValid(parseFfprobeDuration(...)). The html5ify-fastest dummy-file strategy needs a valid duration to construct a seekable preview, so an unknown/invalid duration aborts the conversion attempt. It is re-thrown as a UserFacingError to bypass the generic 'playback failed' fallback.

Source

Thrown at src/renderer/src/App.tsx:2297

            // but in that case we also get: "DEMUXER_ERROR_COULD_NOT_PARSE: FFmpegDemuxer: PTS is not defined 4"
            // and we don't want to auto convert in that case:
            && !error.message?.startsWith('DEMUXER_ERROR_COULD_NOT_PARSE')
          )
          || error.code === PIPELINE_ERROR_DECODE
        )
        && !usingPreviewFile // if we are already using preview file, we shouldn't try to do it again
        && filePath
        && !(error.code === MEDIA_ERR_SRC_NOT_SUPPORTED && error.message?.startsWith('DEMUXER_ERROR_COULD_NOT_PARSE'))
      ) {
        if (workingRef.current) return;
        try {
          setWorking({ text: i18n.t('Converting to supported format') });

          console.log('Trying to convert to supported format');

          // A valid duration is needed to create a html5ified dummy (`fastest`).
          if (!isDurationValid(parseFfprobeDuration(mainFileFormat?.duration))) {
            throw new UserFacingError(i18n.t('Invalid duration'));
          }

          if (hasVideo || hasAudio) {
            await html5ifyAndLoadWithPreferences(customOutDir, filePath, 'fastest', hasVideo, hasAudio);
            showNotNativelySupportedMessage();
          }
        } catch (err) {
          if (err instanceof UserFacingError) {
            throw err;
          }
          console.error(err);
          showPlaybackFailedMessage();
        } finally {
          setWorking(undefined);
        }
      } else if (error.code === PIPELINE_ERROR_READ) { // file is not readable or was removed
        getSwal().toast.fire({ icon: 'error', timer: 10000, text: i18n.t('Failed to read file. Perhaps it has been moved?') });
      }

View on GitHub (pinned to 3b9a59c288)

Solutions

  1. Re-mux the file so ffprobe reports a duration: `ffmpeg -i in -c copy out.mkv`.
  2. Provide the duration manually if the app allows it, or disable the html5ify preview to use direct playback.
  3. If the file is a live stream, stop recording and finalize the container before opening.
  4. Repair the container (e.g. untrunc for MP4) to restore the duration metadata.

Example fix

// before
if (!isDurationValid(parseFfprobeDuration(mainFileFormat?.duration))) {
  throw new UserFacingError(i18n.t('Invalid duration'));
}

// after
const duration = parseFfprobeDuration(mainFileFormat?.duration);
if (!isDurationValid(duration)) {
  showPlaybackFailedMessage(); // skip html5ify, let user try native playback
  return;
}
Defensive patterns

Strategy: validation

Validate before calling

import { isDurationValid, parseFfprobeDuration } from './duration';
const duration = parseFfprobeDuration(mainFileFormat?.duration);
if (!isDurationValid(duration)) {
  // skip html5ify, do not throw
  showPlaybackFailedMessage();
  return;
}

Type guard

const hasValidDuration = (fmt: { duration?: unknown }): boolean => {
  const d = typeof fmt?.duration === 'string' ? parseFloat(fmt.duration) : fmt?.duration;
  return typeof d === 'number' && Number.isFinite(d) && d > 0;
};

Try / catch

try {
  await html5ifyAndLoadWithPreferences(...);
} catch (err) {
  if (err instanceof UserFacingError && /Invalid duration/.test(err.message)) {
    showPlaybackFailedMessage();
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: A media file the browser cannot play natively triggers the recovery path, but ffprobe reported no duration (format.duration undefined/NaN/'N/A'); a live stream or growing file whose duration is not yet known; a container ffprobe read without a duration field.

Common situations: Uncommon containers (e.g. some MKV/AV1/HEVC variants) the browser refuses, combined with a missing duration; live streams; partially-recorded files still open in another process; corrupted headers where ffprobe parsed format but not duration.

Related errors


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