How to Handle Video Errors with video-use: A Complete Guide to Robust FFmpeg Pipelines

Wrap every subprocess.run invocation in helpers/render.py with a try/except block to catch CalledProcessError and provide user-friendly messages instead of allowing the pipeline to crash with a Python traceback.

When processing video with video-use, the pipeline relies heavily on FFmpeg invoked via subprocess.run to extract segments, detect HDR metadata, and composite final outputs. If source files are missing, codecs are unsupported, or filter graphs fail, the default behavior is an immediate crash with a CalledProcessError. Understanding how to handle video errors with video-use requires knowing where the pipeline is fragile and how to inject resilience into the subprocess-heavy workflow.

Understanding the Error Handling Architecture in video-use

The codebase delegates heavy lifting to FFmpeg, creating specific failure points across the rendering pipeline defined in helpers/render.py.

Input Validation and Early Exits

The CLI entry point performs minimal validation. At lines 78-80, the script checks if the EDL file exists and calls sys.exit immediately if not, printing a clear "edl not found" message. However, this validation does not extend to the source media files referenced within the EDL.

FFmpeg Subprocess Calls and Uncaught Exceptions

Most critical operations use subprocess.run(..., check=True), which raises CalledProcessError when FFmpeg returns a non-zero exit code. The following functions lack error handling:

  • Segment extraction (lines 161-210): Long-running FFmpeg commands for scaling, grading, and audio fades.
  • Concatenation (lines 66-82): Uses the concat demuxer; any error bubbles up and stops execution.
  • Final compositing (lines 506-570): Overlays and subtitles stitched together; filter graph errors cause immediate abortion.

In all these cases, the script does not catch the exception, exposing the user to a raw Python stack trace.

Graceful Fallbacks for HDR and Portrait Detection

Not all operations crash on failure. The HDR detection logic (lines 121-132) and portrait detection (lines 134-147) wrap ffprobe calls in try/except blocks that return False on failure. This allows the pipeline to fall back to SDR processing or default aspect ratios without terminating, a pattern you should replicate for other optional features.

Step-by-Step Error Handling Strategies

To make the pipeline production-ready, implement these defensive patterns derived from the source code.

Pre-flight Source File Verification

Before invoking the render pipeline, verify that every source referenced in the EDL exists. The current code at lines 87-92 uses resolve_path to turn relative paths into absolute ones, but performs no existence checks on the resulting paths.

Insert this validation near the start of your main() function:

from pathlib import Path
import sys

def verify_sources(edl: dict, edit_dir: Path) -> None:
    """Verify every source file exists before rendering starts."""
    missing = []
    for name, path_str in edl["sources"].items():
        path = resolve_path(path_str, edit_dir)
        if not path.is_file():
            missing.append(str(path))
    if missing:
        sys.exit(f"❌ Missing source files: {', '.join(missing)}")

Wrapping FFmpeg Calls with Safe Runners

Replace direct subprocess.run calls in extract_segment, concat_segments, build_final_composite, and apply_loudnorm_two_pass with a wrapper that catches CalledProcessError:

import subprocess

def run_ffmpeg(cmd: list[str], quiet: bool = False) -> None:
    """Execute FFmpeg with friendly error handling."""
    try:
        subprocess.run(
            cmd, 
            check=True,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.PIPE
        )
    except subprocess.CalledProcessError as exc:
        print(f"❌ ffmpeg error (code {exc.returncode}): {' '.join(cmd)}")
        sys.exit(exc.returncode)

This guarantees that any FFmpeg failure produces a clean exit with a descriptive message rather than a traceback.

Implementing Graceful Fallbacks for Missing Overlays

Overlays are resolved with resolve_path at lines 515-520 but are not validated before being added to the filter graph. Add existence checks to skip missing overlays with a warning:

def load_overlays(overlays: list[dict], edit_dir: Path) -> list[dict]:
    """Filter out missing overlay files before compositing."""
    valid = []
    for ov in overlays:
        ov_path = resolve_path(ov["file"], edit_dir)
        if ov_path.is_file():
            valid.append(ov)
        else:
            print(f"⚠️  Skipping missing overlay: {ov_path}")
    return valid

Capturing Stderr for Debugging

The current commands discard stdout. For post-mortem analysis, capture stderr to a log file by modifying the subprocess.run calls:

subprocess.run(
    cmd, 
    check=True, 
    stdout=subprocess.DEVNULL,
    stderr=open("ffmpeg_error.log", "a")
)

This preserves FFmpeg's detailed error output for troubleshooting codec or filter graph issues.

Handling Loudness Normalization Failures

The loudness normalization at lines 297-340 implements a two-pass measurement that can fail. The code already falls back to a one-pass approximation by calling apply_loudnorm_two_pass(..., preview=True) when measurement fails. Ensure this fallback path is preserved and consider logging a warning when the approximation is used.

Production-Ready Code Examples

Drop these complete snippets into your workflow to catch typical video-related errors.

Verify sources before rendering:

def verify_sources(edl: dict, edit_dir: Path) -> None:
    missing = []
    for name, path_str in edl["sources"].items():
        path = resolve_path(path_str, edit_dir)
        if not path.is_file():
            missing.append(str(path))
    if missing:
        sys.exit(f"❌ Missing source files: {', '.join(missing)}")

Safe wrapper for all FFmpeg calls:

def run_ffmpeg(cmd: list[str], quiet: bool = False) -> None:
    try:
        subprocess.run(cmd, check=True,
                       stdout=subprocess.DEVNULL,
                       stderr=subprocess.PIPE)
    except subprocess.CalledProcessError as exc:
        print(f"❌ ffmpeg error (code {exc.returncode}): {' '.join(cmd)}")
        sys.exit(exc.returncode)

Graceful overlay handling:

def load_overlays(overlays: list[dict], edit_dir: Path) -> list[dict]:
    valid = []
    for ov in overlays:
        ov_path = resolve_path(ov["file"], edit_dir)
        if ov_path.is_file():
            valid.append(ov)
        else:
            print(f"⚠️  Skipping missing overlay: {ov_path}")
    return valid

Summary

  • video-use delegates all video processing to FFmpeg via subprocess.run, making CalledProcessError the primary failure mode.
  • Critical paths in helpers/render.py (lines 66-82, 161-210, 506-570) lack exception handling and will crash with stack traces on any FFmpeg error.
  • HDR and portrait detection (lines 121-147) already implement graceful fallbacks by returning False on ffprobe failures.
  • Pre-flight validation of EDL sources prevents late-stage crashes during segment extraction.
  • Wrapping subprocess.run calls with try/except blocks provides user-friendly error messages and controlled exit codes for CI/CD pipelines.
  • Validating overlay files after resolve_path prevents filter graph construction errors.

Frequently Asked Questions

What error does video-use throw when FFmpeg fails?

When FFmpeg returns a non-zero exit code, video-use raises a Python subprocess.CalledProcessError because the code uses subprocess.run(..., check=True) without exception handling in functions like extract_segment and concat_segments. This results in a raw stack trace and immediate process termination.

How do I prevent video-use from crashing on missing source files?

Insert a pre-flight check that validates every path in edl["sources"] before calling the render functions. The current codebase only validates the EDL file itself at lines 78-80, not the media files it references. Use the verify_sources pattern shown above to catch missing files before FFmpeg execution begins.

Can I skip HDR detection if ffprobe fails?

Yes. The existing code at lines 121-132 already handles ffprobe failures by wrapping the call in a try/except block that returns False, causing the pipeline to treat the video as SDR. You can add a warning log inside the except block to notify users while allowing the process to continue.

Where should I add error handling in the video-use pipeline?

Focus on helpers/render.py, specifically:

  • Lines 66-82: Wrap the concat demuxer call.
  • Lines 161-210: Wrap the segment extraction FFmpeg command.
  • Lines 515-520: Validate overlay file existence after path resolution.
  • Lines 506-570: Wrap the final compositing command.

These locations represent the highest-risk subprocess calls where missing files or invalid parameters will cause immediate failures.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →