{"record":{"id":"fa18c59f452436c7","repo":"affaan-m/ECC","slug":"label-must-be-finite-and-positive","errorCode":null,"errorMessage":"{label} must be finite and positive","messagePattern":"(.+?) must be finite and positive","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/apply.py","lineNumber":45,"sourceCode":"\nclass ProviderDisabledError(RuntimeError):\n    \"\"\"Provider generation was requested but is not authorized.\"\"\"\n\n\n_FAIL_CLOSED = (\n    \"provider generation requires explicit separately authorized execution; \"\n    \"this package ships no provider adapters and performs no network calls. \"\n    \"Use apply_local() (deterministic, offline) instead.\"\n)\n\n\ndef _utc_now() -> str:\n    return datetime.now(timezone.utc).strftime(\"%Y-%m-%dT%H:%M:%SZ\")\n\n\ndef _positive(value: Any, label: str) -> float:\n    if isinstance(value, bool):\n        raise ValueError(f\"{label} must be finite and positive\")\n    try:\n        number = float(value)\n    except (TypeError, ValueError, OverflowError) as exc:\n        raise ValueError(f\"{label} must be finite and positive\") from exc\n    if not math.isfinite(number) or number <= 0:\n        raise ValueError(f\"{label} must be finite and positive\")\n    return number\n\n\ndef _strict_assign(\n    planned: list[float], media: list[dict[str, Any]], target: float, fps: float\n) -> list[tuple[dict[str, Any], int]]:\n    \"\"\"Fill the target frame count, then match whole shots to unique sources.\"\"\"\n    target_frames = timeline.seconds_to_frames(target, fps)\n    if target_frames < 1:\n        raise ValueError(\"target duration must contain at least one frame\")\n    frame_counts = []\n    elapsed = 0.0","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/apply.py#L27-L63","documentation":"`_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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nplan_shots({\"shots\": [{\"duration\": True}]}, 12.0)\n// after\nplan_shots({\"shots\": [{\"duration\": 0.85}]}, 12.0)","handlingStrategy":"validation","validationCode":"def valid_positive(v):\n    return isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(v) and v > 0\nassert all(valid_positive(s[\"duration\"]) for s in cadence.get(\"shots\", []))","typeGuard":"def is_positive_number(v) -> bool:\n    return isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(float(v)) and float(v) > 0","tryCatchPattern":null,"preventionTips":["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"],"tags":["validation","numeric","bool"],"backgroundTag":"invalid-argument-value","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}