{"record":{"id":"bfc3b46473fd46ac","repo":"affaan-m/ECC","slug":"fps-must-be-finite-and-positive-forge","errorCode":null,"errorMessage":"fps must be finite and positive","messagePattern":"fps must be finite and positive","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/forge.py","lineNumber":77,"sourceCode":"    duration: float | None = None,\n    strength: float = 1.0,\n    grade_base: bool = True,\n    width: int | None = None,\n    height: int | None = None,\n    work: str = \"out/forge_work\",\n    plan: list[dict] | None = None,\n    fps: float | None = None,\n) -> Path:\n    out_path = Path(out)\n    validate_output(out_path)\n    if not takes or any(not Path(t).is_file() for t in takes):\n        raise ValueError(\"all takes must be readable local files\")\n    if base_video and not Path(base_video).is_file():\n        raise ValueError(\"base video must be a readable local file\")\n    if any(not Path(o).is_file() for o in (overlays or [])):\n        raise ValueError(\"all overlays must be readable local files\")\n    if fps is not None and (not math.isfinite(fps) or fps <= 0):\n        raise ValueError(\"fps must be finite and positive\")\n    sp = pack_mod.load(genre, root=root)\n    tgt = grade_mod.load_stats(sp.grade_path)\n    cad = cad_mod.load(sp.cadence_path)\n\n    # Geometry comes from the first take unless overridden; everything else is\n    # normalized to it so concat does not silently fail on a size mismatch.\n    info0 = frame_mod.probe(takes[0])\n    W = width or info0.width\n    H = height or info0.height\n    FPS = fps if fps is not None else info0.fps\n    if not math.isfinite(FPS) or FPS <= 0:\n        raise ValueError(\"source fps must be finite and positive; provide --fps\")\n    if W <= 0 or H <= 0:\n        raise ValueError(\"output width and height must be positive\")\n    # Prior timelines reference these shot files. Each run owns a fresh child,\n    # including failed runs, so retries cannot erase an existing edit.\n    work_root = Path(work)\n    work_root.mkdir(parents=True, exist_ok=True)","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/forge.py#L59-L95","documentation":"forge() accepts an optional fps override; when provided it must be a finite number greater than zero. NaN, infinity, zero, or negative values would produce invalid ffmpeg output, so it raises ValueError immediately.","triggerScenarios":"Calling forge(..., fps=0) or fps=-30; passing float('nan') or float('inf') (e.g. from a failed parse of a config/CLI value); passing a string that was never converted to float.","commonSituations":"Parsing fps from a config file where the value is 'auto' or empty and a float() call yields NaN via data paths; dividing by zero when computing fps from frame counts; hand-editing a YAML/JSON pipeline with fps: 0.","solutions":["Pass a positive finite fps, e.g. fps=24.0.","Omit fps entirely to let forge use the first take's probed fps.","Sanitize parsed values: reject non-finite and non-positive numbers before calling forge.","Fix the config/CLI source so it provides a numeric frame rate."],"exampleFix":"# before\nfps = float(cfg.get('fps', 'nan'))\nforge('noir', takes, fps=fps, out='edit.mp4')  # ValueError\n\n# after\nfps = cfg.get('fps')\nfps = float(fps) if fps and math.isfinite(float(fps)) and float(fps) > 0 else None\nforge('noir', takes, fps=fps, out='edit.mp4')  # None -> probe take fps","handlingStrategy":"validation","validationCode":"import math\n\ndef fps_ok(fps):\n    return fps is None or (isinstance(fps, (int, float)) and math.isfinite(fps) and fps > 0)","typeGuard":"def is_valid_fps(v):\n    return isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(v) and v > 0","tryCatchPattern":"try:\n    forge(genre, takes, fps=fps, out=out)\nexcept ValueError as e:\n    if 'fps' in str(e):\n        forge(genre, takes, out=out)  # drop override, probe from take","preventionTips":["Validate numeric overrides with math.isfinite and > 0 before calling.","Omit fps to let forge probe the first take's frame rate.","Guard config parsing against 'auto'/empty values producing NaN."],"tags":["validation","numeric","cli","fps"],"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"}