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
- Re-mux the file so ffprobe reports a duration: `ffmpeg -i in -c copy out.mkv`.
- Provide the duration manually if the app allows it, or disable the html5ify preview to use direct playback.
- If the file is a live stream, stop recording and finalize the container before opening.
- 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
- Re-mux sources so ffprobe reports a duration before opening them.
- Finalize live recordings before opening (stop the writer so the container gets its duration).
- Provide a manual duration override when the container omits one.
- Repair broken containers (untrunc, re-mux) to restore duration metadata.
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.