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
- Ensure settings.timelineFrameRate exists and is a clean label ("23.976", "24", "24000/1001").
- Make the exact resolved rate equal baseline.fps (remember 23.976 means 24000/1001).
- Re-export the snapshot from the intended Resolve timeline.
- 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
- Run a preflight fps check before invoking the library.
- Keep one canonical fps representation per project.
- Re-export snapshots after any project frame-rate change.
- Test the full _snapshot path with real exports, not synthetic JSON.
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
- native fps missing
- candidate fps/duration/retime ambiguity
- fps must be a reduced positive rational
- fps must be finite and positive
- fps must be finite and positive
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)