affaan-m/ECC · error · ValueError
must be finite and positive
Error message
{label} must be finite and positive What it means
`_positive()` validates that a numeric value is a finite, strictly positive float. It rejects Python bools explicitly (since `bool` is a subclass of `float` and would silently coerce to 0/1), rejects values that cannot be converted to float, and rejects NaN, infinities, zero, and negatives. It is used by `plan_shots` and `apply_local` to validate shot durations and frame rates.
Solutions
- Inspect the value passed for the failing label (e.g. 'cadence shot duration') and make it a positive finite number
- Remove boolean literals from config fields that should hold numeric durations
- Convert user/env input with float() and validate `math.isfinite(x) and x > 0` before calling plan_shots/apply_local
- Fix the upstream cadence.json generation so measured shot durations are never 0, negative, or NaN
Example fix
// before
plan_shots({"shots": [{"duration": True}]}, 12.0)
// after
plan_shots({"shots": [{"duration": 0.85}]}, 12.0) Defensive patterns
Strategy: validation
Validate before calling
def valid_positive(v):
return isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(v) and v > 0
assert all(valid_positive(s["duration"]) for s in cadence.get("shots", [])) Type guard
def is_positive_number(v) -> bool:
return isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(float(v)) and float(v) > 0 Prevention
- Never allow boolean literals in numeric config fields (validate JSON schemas with 'type': 'number')
- Parse and validate all durations/fps at config-load time with math.isfinite checks
- Normalize units to seconds at the boundary before calling the library
When it happens
Trigger: Calling `plan_shots(cadence, target_duration)` or `apply_local(...)` with a duration or fps value that is a bool (`True`/`False`), a non-numeric string, None, NaN, infinity, zero, or a negative number, at which point `_positive` raises at line 45 (the bool check).
Common situations: JSON configs where a field was set to `true` instead of a number; YAML parse of `yes`/`on` as boolean; env-var strings like "30fps" that fail float(); JSON config missing a field (None); computed durations that end up 0 due to upstream rounding or div by large numbers; corrupted cadence.json containing NaN/Infinity.
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 finite and positive
- Geometry, fps and duration must be finite and positive
- contains a non-finite numeric value
- has an invalid finite source duration
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/fa18c59f452436c7.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/apply.py:45
class ProviderDisabledError(RuntimeError):
"""Provider generation was requested but is not authorized."""
_FAIL_CLOSED = (
"provider generation requires explicit separately authorized execution; "
"this package ships no provider adapters and performs no network calls. "
"Use apply_local() (deterministic, offline) instead."
)
def _utc_now() -> str:
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
def _positive(value: Any, label: str) -> float:
if isinstance(value, bool):
raise ValueError(f"{label} must be finite and positive")
try:
number = float(value)
except (TypeError, ValueError, OverflowError) as exc:
raise ValueError(f"{label} must be finite and positive") from exc
if not math.isfinite(number) or number <= 0:
raise ValueError(f"{label} must be finite and positive")
return number
def _strict_assign(
planned: list[float], media: list[dict[str, Any]], target: float, fps: float
) -> list[tuple[dict[str, Any], int]]:
"""Fill the target frame count, then match whole shots to unique sources."""
target_frames = timeline.seconds_to_frames(target, fps)
if target_frames < 1:
raise ValueError("target duration must contain at least one frame")
frame_counts = []
elapsed = 0.0View on GitHub (pinned to 8321021c54)