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
- Pass a positive finite number, e.g. fps=29.97 or fps='30000/1001' for exact NTSC rates
- Guard upstream: if not (fps > 0) or not math.isfinite(fps): raise/skip before calling resolve functions
- 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
- Read fps via ffprobe and fail early when it is 0 or missing
- Never pass None or bool into fps parameters
- Use fractional strings like '30000/1001' for exact NTSC rates
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
- fps must be finite and positive
- fps must be positive, got
- target timeline fps mismatch
- base_track_count exceeds target video tracks
- candidate fps/duration/retime ambiguity
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)