affaan-m/ECC · error · ValueError

variable or ambiguous frame rate

Error message

variable or ambiguous frame rate: {path}

What it means

After probing, probe_asset compares the stream's avg_frame_rate with r_frame_rate; it raises ValueError('variable or ambiguous frame rate: {path}') when they differ or either cannot be normalized to the same fps. This guards compositing math that assumes a constant frame rate — VFR input would desynchronize placements.

Solutions

  1. Re-encode the asset to constant frame rate matching the timeline: `ffmpeg -i in.mp4 -vf fps=30 -vsync cfr out.mp4`
  2. Or run `ffmpeg -i in.mp4 -enc_q -vsync cfr -r 30 out.mp4` to force CFR without re-filtering
  3. Use source footage recorded at constant frame rate (set OBS to CFR) instead of VFR recordings
  4. If you believe the rates are actually equivalent (e.g. 30/1 vs 30000/1001 rounding), normalize them yourself — the library requires exact equality after normalization

Example fix

# before
info = resolve.probe_asset('screen_recording.mov')  # VFR
# after
subprocess.run(['ffmpeg', '-i', 'screen_recording.mov', '-vsync', 'cfr', '-r', '30', 'cfr.mov'])
info = resolve.probe_asset('cfr.mov')
Defensive patterns

Strategy: validation

Validate before calling

import json, subprocess

def is_constant_rate(path):
    r = subprocess.run(['ffprobe', '-v', 'error', '-select_streams', 'v:0',
                        '-show_entries', 'stream=avg_frame_rate,r_frame_rate',
                        '-of', 'json', str(path)], capture_output=True, text=True)
    s = json.loads(r.stdout)['streams'][0]
    return s['avg_frame_rate'] == s['r_frame_rate']

assert is_constant_rate('cfr.mov')

Try / catch

try:
    info = resolve.probe_asset(path)
except ValueError as e:
    if 'variable or ambiguous frame rate' in str(e):
        cfr = conform_to_cfr(path, fps=30)
        info = resolve.probe_asset(cfr)
    else:
        raise

Prevention

When it happens

Trigger: Probing screen recordings or phone footage encoded with variable frame rate (avg_frame_rate='30000/1001' vs r_frame_rate='60000/1001'); probing files where ffprobe reports r_frame_rate like '0/0' or '1000/1' inconsistent with the average.

Common situations: OBS/phone screen recordings (commonly VFR); footage re-cut in an editor that produced VFR output; mkv files with default/inherited per-frame rates that disagree with the average.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/f8a0ab84efdd2716. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/resolve.py:66

            "-count_frames",
            "-show_entries",
            "stream=avg_frame_rate,r_frame_rate,nb_read_frames,pix_fmt",
            "-of",
            "json",
            str(path),
        ],
        capture_output=True,
        text=True,
        check=True,
        timeout=120,
    )
    streams = json.loads(result.stdout).get("streams", [])
    if len(streams) != 1:
        raise ValueError(f"asset must contain a video stream: {path}")
    stream = streams[0]
    average = _fps(stream["avg_frame_rate"])
    if average != _fps(stream["r_frame_rate"]):
        raise ValueError(f"variable or ambiguous frame rate: {path}")
    pixel_format = stream.get("pix_fmt", "")
    alpha = pixel_format.startswith(("yuva", "gbrap")) or pixel_format in {
        "rgba",
        "bgra",
        "argb",
        "abgr",
        "rgba64be",
        "rgba64le",
        "bgra64be",
        "bgra64le",
        "ya8",
        "ya16be",
        "ya16le",
    }
    return {
        "fps": str(average),
        "frames": int(stream["nb_read_frames"]),
        "has_alpha": alpha,

View on GitHub (pinned to 8321021c54)