affaan-m/ECC · error · ValueError

fps must be finite and positive

Error message

fps must be finite and positive

What it means

resolve._fps normalizes an fps value (accepting int, float, or fractional string like '30000/1001') through Fraction and rejects anything non-finite or <= 0 with 'fps must be finite and positive'. Booleans are rejected outright since bool is an int subclass. It is used by probe_asset, allocate_placements, and apply_placements.

Solutions

  1. Pass a positive finite number, e.g. fps=29.97 or fps='30000/1001' for exact NTSC rates
  2. Guard upstream: if not (fps > 0) or not math.isfinite(fps): raise/skip before calling resolve functions
  3. Fix the probe step so asset fps is read correctly (ffprobe) instead of defaulting to 0/None

Example fix

// before
apply_placements(asset, fps=None)  # raises
// after
fps = probe_asset(path).fps
if not fps or not math.isfinite(fps) or fps <= 0:
    fps = 30.0
apply_placements(asset, fps=fps)
Defensive patterns

Strategy: validation

Validate before calling

import math
ok = (fps is not None and not isinstance(fps, bool)
      and math.isfinite(float(fps)) and float(fps) > 0)
assert ok, f"fps must be finite and positive, got {fps!r}"

Try / catch

try:
    result = allocate_placements(..., fps=fps)
except ValueError:
    fps = 30.0  # safe default for corrupt/missing probe metadata
    result = allocate_placements(..., fps=fps)

Prevention

When it happens

Trigger: Passing fps=0, fps=-29.97, fps=float('nan') or inf, fps=True/False, or a value whose Fraction(str(value)) conversion raises TypeError/ZeroDivisionError (e.g. '1/0', None, a list); also unparseable strings fall into the except branch.

Common situations: Probe metadata returning 0 fps for a corrupt media file; dividing by a missing duration to compute fps producing inf/nan; config where fps is null; copy-pasted fractional strings like '30000/1001 ' with stray characters.

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/8702cd7edefdf7d5. Report an issue: GitHub.

Appendix: source

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

import copy
import json
import math
import subprocess
from fractions import Fraction
from pathlib import Path

from .timeline import fps_fraction


def _integer(value, label, minimum=0):
    if isinstance(value, bool) or not isinstance(value, int) or value < minimum:
        raise ValueError(f"{label} must be an integer >= {minimum}")
    return value


def _fps(value):
    if isinstance(value, bool):
        raise ValueError("fps must be finite and positive")
    try:
        number = float(Fraction(str(value)))
        if not math.isfinite(number) or number <= 0:
            raise ValueError("fps must be finite and positive")
        return fps_fraction(number)
    except (TypeError, ZeroDivisionError, OverflowError) as exc:
        raise ValueError("invalid fps") from exc


def probe_asset(path):
    """Count decoded video frames; never infer source length from duration."""
    result = subprocess.run(
        [
            "ffprobe",
            "-v",
            "error",
            "-select_streams",
            "v:0",

View on GitHub (pinned to 8321021c54)