affaan-m/ECC · error · ValueError

native snapshot fps is missing, invalid or contradictory

Error message

native snapshot fps is missing, invalid or contradictory

What it means

All fps problems found while reconciling the native snapshot (missing, badly formatted, or contradicting baseline) are consolidated into this single ValueError raised by _snapshot. It is the user-facing error for the whole try block that validates settings.timelineFrameRate against baseline fps.

Solutions

  1. Ensure settings.timelineFrameRate exists and is a clean label ("23.976", "24", "24000/1001").
  2. Make the exact resolved rate equal baseline.fps (remember 23.976 means 24000/1001).
  3. Re-export the snapshot from the intended Resolve timeline.
  4. Validate fps parity with a quick script before running build_application_bundle.

Example fix

// before
"settings": {}  (no fps) while baseline fps = 24000/1001

// after
"settings": {"timelineFrameRate": "23.976"}  # resolves to 24000/1001
Defensive patterns

Strategy: validation

Validate before calling

from fractions import Fraction
def fps_consistent(baseline_fps, snapshot):
    settings = snapshot.get("settings")
    if not isinstance(settings, dict):
        return False
    v = settings.get("timelineFrameRate")
    if type(v) not in (str, int, float):
        return False
    ntsc = {"23.976": "24000/1001", "29.97": "30000/1001", "59.94": "60000/1001"}
    label = str(v)
    import re
    if not re.fullmatch(r"[0-9]{1,9}(?:\.[0-9]{1,12}|/[1-9][0-9]{0,8})?", label):
        return False
    return Fraction(ntsc.get(label, label)) == Fraction(baseline_fps["numerator"], baseline_fps["denominator"])

Try / catch

try:
    bundle = build_application_bundle(config, None, local_only=True)
except ValueError as exc:
    if "fps is missing, invalid or contradictory" in str(exc):
        print("reconcile settings.timelineFrameRate with baseline.fps")
    raise

Prevention

When it happens

Trigger: build_application_bundle -> _snapshot when timelineFrameRate is absent, not str/int/float, fails the label regex, or its resolved Fraction differs from baseline.fps (also ZeroDivisionError from a zero denominator).

Common situations: Snapshot exported from a mismatched project; hand-edited fps values; NTSC decimal vs exact rational confusion; a snapshot missing the settings block entirely.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/integration.py:210

    _artifact(baseline["project_file"])
    snapshot = _artifact(baseline["snapshot_file"], parse_json=True)
    if not isinstance(snapshot, dict):
        raise ValueError("native snapshot must be an object")
    settings = snapshot.get("settings")
    native_fps = settings.get("timelineFrameRate") if isinstance(settings, dict) else None
    # Resolve's conventional decimal NTSC labels represent these exact rates.
    ntsc = {"23.976": "24000/1001", "29.97": "30000/1001", "59.94": "60000/1001"}
    try:
        if type(native_fps) not in (str, int, float):
            raise ValueError("native fps missing")
        label = str(native_fps)
        if not re.fullmatch(r"[0-9]{1,9}(?:\.[0-9]{1,12}|/[1-9][0-9]{0,8})?", label):
            raise ValueError("native fps must use a bounded decimal or rational label")
        native_rate = Fraction(ntsc.get(label, label))
        if native_rate != Fraction(*_rate(baseline["fps"])):
            raise ValueError("native fps differs")
    except (ValueError, ZeroDivisionError) as exc:
        raise ValueError("native snapshot fps is missing, invalid or contradictory") from exc
    for cfg, key in [("project_name", "project"), ("timeline_name", "timeline")]:
        if _text(baseline[cfg]) != snapshot.get(key):
            raise ValueError("native snapshot identity mismatch")
    tracks = snapshot.get("timeline_readback")
    if not isinstance(tracks, dict) or not tracks:
        raise ValueError("native clip snapshot required")
    for track, clips in tracks.items():
        if not re.fullmatch(r"(?:video|audio)[1-9][0-9]*", track) or not isinstance(clips, list):
            raise ValueError("invalid native snapshot track")
        for clip in clips:
            _check_clip(clip, bounds)
    return tracks


def _check_clip(clip: Any, bounds: list[int]) -> None:
    required = {"path", "name", "start", "end", "left_offset", "right_offset", "enabled", "properties"}
    if not isinstance(clip, dict) or not required <= clip.keys():
        raise ValueError("native snapshot clip is incomplete")

View on GitHub (pinned to 8321021c54)