babysor/MockingBird · error · RuntimeError

ffprobe failed on {path}: {proc.stderr}

Error message

ffprobe failed on {path}: {proc.stderr}

What it means

RuntimeError raised by probe_duration_ms when ffprobe (bundled with ffmpeg) exits non-zero while reading a media file's duration. Used before atempo time-stretching and after Kokoro TTS to get actual audio length.

Source

Thrown at skills/speak/scripts/render_timeline.py:135

    if proc.returncode != 0:
        raise RuntimeError(f"ffmpeg failed: {' '.join(cmd)}\n{proc.stderr}")


def ensure_ffmpeg() -> None:
    if not shutil.which("ffmpeg"):
        raise RuntimeError("ffmpeg not found in PATH.")


def probe_duration_ms(path: Path) -> float:
    proc = subprocess.run(
        [
            "ffprobe", "-v", "error", "-show_entries", "format=duration",
            "-of", "default=noprint_wrappers=1:nokey=1", str(path),
        ],
        capture_output=True, text=True,
    )
    if proc.returncode != 0:
        raise RuntimeError(f"ffprobe failed on {path}: {proc.stderr}")
    return float(proc.stdout.strip()) * 1000


def normalize_duration_pad_trim(inp: Path, outp: Path, target_ms: int) -> None:
    """Pad short audio then trim to exact target duration (Noiz backend)."""
    sec = target_ms / 1000.0
    _run_ff([
        "ffmpeg", "-y", "-i", str(inp),
        "-af", f"apad=pad_dur={sec:.3f}",
        "-t", f"{sec:.3f}", str(outp),
    ])


def normalize_duration_atempo(inp: Path, outp: Path, target_ms: int) -> None:
    """Use atempo to stretch/compress audio to target duration (Kokoro backend)."""
    actual_ms = probe_duration_ms(inp)
    if actual_ms <= 0:
        normalize_duration_pad_trim(inp, outp, target_ms)

View on GitHub (pinned to 28dc5e14f1)

Solutions

  1. Run: ffprobe -v error -show_entries format=duration file.wav manually to see the error
  2. Validate the file is real audio (check size > 0 and file type) before probing
  3. Re-generate the offending audio segment
  4. Ensure ffprobe is installed and on PATH (comes with ffmpeg)

Example fix

# before
ms = probe_duration_ms(seg_path)
# after
if not seg_path.exists() or seg_path.stat().st_size == 0:
    raise RuntimeError(f"empty segment: {seg_path}")
ms = probe_duration_ms(seg_path)
Defensive patterns

Strategy: validation

Validate before calling

p = Path(seg)
assert p.exists() and p.stat().st_size > 0, f'bad audio file: {p}'

Try / catch

try:
    ms = probe_duration_ms(p)
except RuntimeError:
    ms = -1.0  # re-synthesize segment later

Prevention

When it happens

Trigger: Pointing ffprobe at a non-media file, a corrupt/truncated audio file (e.g. incomplete TTS download), an unreadable path, or a format the installed ffprobe cannot parse.

Common situations: TTS output was written from an error response body instead of audio, partial file from a killed process, or zero-byte file from a failed upstream request.

Related errors


AI-assisted analysis of babysor/MockingBird@28dc5e14f1 (2026-08-27). Data as JSON: /api/errors/eb13644df19efea9. Report an issue: GitHub.