affaan-m/ECC · error · ValueError

target duration must contain at least one frame

Error message

target duration must contain at least one frame

What it means

`_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`.

Solutions

  1. Increase target_duration so it covers at least one frame (>= 1/fps seconds)
  2. Verify the unit is seconds, not milliseconds or frames
  3. Raise fps so a frame is short enough for the requested duration
  4. Pre-check: `seconds_to_frames(target, fps) >= 1` before calling

Example fix

// before
apply_local(..., target=0.02, fps=30)  # < 1 frame
// after
apply_local(..., target=max(0.02, 1/30), fps=30)
Defensive patterns

Strategy: validation

Validate before calling

if timeline.seconds_to_frames(target, fps) < 1:
    raise ValueError("target duration shorter than one frame")

Try / catch

try:
    report = apply_local(media=media, sp=sp, target=target, fps=fps)
except ValueError as exc:
    if "at least one frame" in str(exc):
        target = 1.0 / fps
        report = apply_local(media=media, sp=sp, target=target, fps=fps)
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/7896c8abf77aaa36. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/apply.py:61

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
    assigned = 0
    for duration in planned:
        if assigned == target_frames:
            break
        elapsed += duration
        boundary = min(target_frames, timeline.seconds_to_frames(elapsed, fps))
        if boundary <= assigned:
            raise ValueError("cadence shot cannot occupy a whole frame")
        frame_counts.append(boundary - assigned)
        assigned = boundary
    if assigned < target_frames:
        frame_counts.append(target_frames - assigned)

    rate = timeline.fps_fraction(fps)
    sources: dict[str, tuple[dict[str, Any], int]] = {}
    for clip in media:

View on GitHub (pinned to 8321021c54)