affaan-m/ECC · error · ValueError

native fps must use a bounded decimal or rational label

Error message

native fps must use a bounded decimal or rational label

What it means

The native snapshot's timelineFrameRate, converted to a string, must match a bounded decimal or rational label: up to 9 digits, optionally followed by 1-12 decimal places or /N rational form. Anything else (scientific notation, negative values, extra text, units) is rejected with this error.

Solutions

  1. Set timelineFrameRate to a plain label: "23.976", "29.97", "59.94", "24", or the rational form "24000/1001".
  2. If the value is a float, round/serialize it deliberately (e.g. format to a fixed decimal string) instead of relying on str(float).
  3. Strip units, whitespace and signs from the fps string in the exporter.
  4. Prefer string or exact rational export from the snapshot producer to avoid float repr artifacts.

Example fix

// before
"timelineFrameRate": 2.3976000000000001e+01

// after
"timelineFrameRate": "23.976"
Defensive patterns

Strategy: validation

Validate before calling

import re
FPS_RE = re.compile(r"[0-9]{1,9}(?:\.[0-9]{1,12}|/[1-9][0-9]{0,8})?")
def check_fps_label(value):
    label = str(value)
    if not FPS_RE.fullmatch(label):
        raise ValueError(f"fps label {label!r} is not a bounded decimal or rational")
    return label

Type guard

def is_bounded_fps_label(value) -> bool:
    return isinstance(value, (str, int, float)) and bool(re.fullmatch(r"[0-9]{1,9}(?:\.[0-9]{1,12}|/[1-9][0-9]{0,8})?", str(value)))

Try / catch

try:
    bundle = build_application_bundle(config, None, local_only=True)
except ValueError as exc:
    if "bounded decimal or rational label" in str(exc):
        print("normalize fps to e.g. '23.976' or '24000/1001'")
    raise

Prevention

When it happens

Trigger: timelineFrameRate stringifies to something like "2.4e+01", "24 fps", "-24", "24.0000000000001" (>12 decimals), "24000/10010" (denominator with leading zero pattern), or a float str() artifact like "23.975999999999999".

Common situations: A pipeline storing fps as a float loses the clean label and str() produces long repr; another tool writes scientific notation; someone edits the snapshot and adds units or whitespace; int-typed fps exported as "24.0 fps".

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

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

def _snapshot(baseline: dict) -> dict:
    _object(baseline, {"project_file", "snapshot_file", "project_name", "timeline_name",
                       "fps", "timeline_range"})
    _rate(baseline["fps"])
    bounds = _range(baseline["timeline_range"])
    _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

View on GitHub (pinned to 8321021c54)