affaan-m/ECC · error · ValueError

fps must be positive, got

Error message

fps must be positive, got {fps!r}

What it means

fps_fraction converts an fps value to an exact Fraction, snapping common NTSC decimals (23.976, 29.97, ...) to their exact ratios. It rejects any fps that is not strictly positive with this ValueError, including 0, negative numbers, NaN, and non-numeric types that cannot float()-convert cleanly (TypeError) or evaluate to <= 0.

Solutions

  1. Pass a positive numeric fps (e.g. float from timeline.GetSetting('timelineFrameRate')) and validate > 0 before use.
  2. Handle missing timeline settings by defaulting to the project frame rate: proj.GetSetting('timelineFrameRate').
  3. For NTSC rates, pass the exact decimal (23.976, 29.97) or a Fraction — the function snaps within tolerance.

Example fix

// before
fps = tl.GetSetting("timelineFrameRate") or 0
frac = fps_fraction(fps)
// after
fps = float(tl.GetSetting("timelineFrameRate") or proj.GetSetting("timelineFrameRate"))
if fps <= 0:
    raise ValueError(f"timeline reports invalid fps: {fps!r}")
frac = fps_fraction(fps)
Defensive patterns

Strategy: type-guard

Validate before calling

def safe_fps(raw):
    try:
        value = float(raw)
    except (TypeError, ValueError):
        return None
    return value if value > 0 else None

fps = safe_fps(timeline.GetSetting("timelineFrameRate")) or safe_fps(project.GetSetting("timelineFrameRate"))

Type guard

def is_positive_fps(value):
    try:
        return float(value) > 0 and float(value) == float(value)  # excludes NaN
    except (TypeError, ValueError):
        return False

Try / catch

try:
    frac = fps_fraction(fps)
except (ValueError, TypeError) as e:
    print(f"fps {fps!r} unusable: {e}; fall back to project frame rate")
    frac = fps_fraction(float(project.GetSetting("timelineFrameRate")))

Prevention

When it happens

Trigger: Calling fps_fraction(0), fps_fraction(-24), fps_fraction(float('nan')), or passing None/'' so float() yields 0.0 or raises; calling _format_name/_tc_to_frames with a timeline setting that returned an empty or zero string.

Common situations: timeline.GetSetting('timelineFrameRate') returning '' or '0' on an unsaved/new timeline; a config file with fps: 0 as a placeholder; forgetting to default fps when a project setting is missing.

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/4c05588ca11ca410. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/timeline.py:39

# 29.97 is exactly 30000/1001; a decimal timebase drifts ~3.6s/hour.
_NTSC: dict[float, Fraction] = {
    23.976: Fraction(24000, 1001),
    29.97: Fraction(30000, 1001),
    47.952: Fraction(48000, 1001),
    59.94: Fraction(60000, 1001),
    119.88: Fraction(120000, 1001),
}
_NTSC_TOL = 0.02


def fps_fraction(fps: float | Fraction) -> Fraction:
    """Exact frame rate as a Fraction, snapping NTSC-family decimals."""
    if isinstance(fps, Fraction):
        return fps
    fps = float(fps)
    if fps <= 0:
        raise ValueError(f"fps must be positive, got {fps!r}")
    for nominal, exact in _NTSC.items():
        if abs(fps - nominal) < _NTSC_TOL:
            return exact
    if abs(fps - round(fps)) < 1e-9:
        return Fraction(int(round(fps)), 1)
    return Fraction(fps).limit_denominator(100000)


def frame_duration(fps: float | Fraction) -> Fraction:
    """Duration of one frame, in seconds, as an exact fraction."""
    return 1 / fps_fraction(fps)


def seconds_to_frames(seconds: float, fps: float | Fraction) -> int:
    """Quantise seconds to whole frames, rounding half away from zero."""
    f = fps_fraction(fps)
    exact = Fraction(float(seconds)).limit_denominator(1_000_000) * f
    floor = exact.numerator // exact.denominator

View on GitHub (pinned to 8321021c54)