{"record":{"id":"7896c8abf77aaa36","repo":"affaan-m/ECC","slug":"target-duration-must-contain-at-least-one-frame","errorCode":null,"errorMessage":"target duration must contain at least one frame","messagePattern":"target duration must contain at least one frame","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/apply.py","lineNumber":61,"sourceCode":"def _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\n    assigned = 0\n    for duration in planned:\n        if assigned == target_frames:\n            break\n        elapsed += duration\n        boundary = min(target_frames, timeline.seconds_to_frames(elapsed, fps))\n        if boundary <= assigned:\n            raise ValueError(\"cadence shot cannot occupy a whole frame\")\n        frame_counts.append(boundary - assigned)\n        assigned = boundary\n    if assigned < target_frames:\n        frame_counts.append(target_frames - assigned)\n\n    rate = timeline.fps_fraction(fps)\n    sources: dict[str, tuple[dict[str, Any], int]] = {}\n    for clip in media:","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/apply.py#L43-L79","documentation":"`_strict_assign` converts the requested target duration into an integer frame count using `timeline.seconds_to_frames(target, fps)`. If the result is < 1, the target duration cannot hold even a single frame at the given frame rate, so there is nothing to assign and it raises. Called by `apply_local`.","triggerScenarios":"Calling `apply_local` with a target duration shorter than one frame period (e.g. 0.02s at 30fps, where one frame ≈ 0.0333s), or a duration that rounds down to 0 frames.","commonSituations":"User asks for a 1-frame-looking clip but passes duration in milliseconds instead of seconds; very low fps values making frame periods long; target computed from another unit conversion error.","solutions":["Increase target_duration so it covers at least one frame (>= 1/fps seconds)","Verify the unit is seconds, not milliseconds or frames","Raise fps so a frame is short enough for the requested duration","Pre-check: `seconds_to_frames(target, fps) >= 1` before calling"],"exampleFix":"// before\napply_local(..., target=0.02, fps=30)  # < 1 frame\n// after\napply_local(..., target=max(0.02, 1/30), fps=30)","handlingStrategy":"validation","validationCode":"if timeline.seconds_to_frames(target, fps) < 1:\n    raise ValueError(\"target duration shorter than one frame\")","typeGuard":null,"tryCatchPattern":"try:\n    report = apply_local(media=media, sp=sp, target=target, fps=fps)\nexcept ValueError as exc:\n    if \"at least one frame\" in str(exc):\n        target = 1.0 / fps\n        report = apply_local(media=media, sp=sp, target=target, fps=fps)\n    else:\n        raise","preventionTips":["Pre-check frame count >= 1 for any user-supplied duration","Ensure durations are in seconds, not milliseconds or frames","Document the minimum representable duration (1/fps) in your CLI/config"],"tags":["timeline","frames","duration"],"backgroundTag":"value-out-of-range","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"}