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

  1. Inspect the value passed for the failing label (e.g. 'cadence shot duration') and make it a positive finite number
  2. Remove boolean literals from config fields that should hold numeric durations
  3. Convert user/env input with float() and validate `math.isfinite(x) and x > 0` before calling plan_shots/apply_local
  4. 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

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


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.0

View on GitHub (pinned to 8321021c54)