affaan-m/ECC · error · ValueError

must be an integer >=

Error message

{label} must be an integer >= {minimum}

What it means

resolve._integer validates that a numeric option is a real int (not a bool) and at least the given minimum, raising ValueError '<label> must be an integer >= <minimum>' otherwise. It backs allocate_placements and other placement resolution entry points.

Solutions

  1. Convert with a strict check: int(value) after verifying it is not a float with a fractional part, or round() a computed float
  2. Pass actual ints (e.g. frames=round(duration * fps)) into allocate_placements
  3. Fix upstream config parsing to coerce numeric fields to int before reaching resolve

Example fix

// before
allocate_placements(frames="12", ...)  # string
// after
allocate_placements(frames=int("12"), ...)  # or max(1, round(duration * fps))
Defensive patterns

Strategy: validation

Validate before calling

def is_count(v):
    return isinstance(v, int) and not isinstance(v, bool) and v >= 0
assert is_count(frames), f"frames must be a non-negative int, got {frames!r}"

Try / catch

try:
    placements = allocate_placements(frames=frames, ...)
except ValueError as e:
    frames = max(1, round(float(frames)))
    placements = allocate_placements(frames=frames, ...)

Prevention

When it happens

Trigger: Passing frames=10.5 or frames="10" (non-int), frames=True (bool is explicitly rejected), or a value below the minimum such as frames=0 when minimum=1; passing None.

Common situations: Reading numeric options from JSON/YAML where they deserialize as floats or strings; using True/False as a count by mistake; computing a duration*fps in float and forgetting to round; CLI parsing that yields strings.

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/7699e559b68f5190. Report an issue: GitHub.

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/resolve.py:23

receipt; partial edits may remain and must be discarded/restored by the caller.
Composite values are explicit API integers, never guessed blend-mode names.
"""

from __future__ import annotations

import copy
import json
import math
import subprocess
from fractions import Fraction
from pathlib import Path

from .timeline import fps_fraction


def _integer(value, label, minimum=0):
    if isinstance(value, bool) or not isinstance(value, int) or value < minimum:
        raise ValueError(f"{label} must be an integer >= {minimum}")
    return value


def _fps(value):
    if isinstance(value, bool):
        raise ValueError("fps must be finite and positive")
    try:
        number = float(Fraction(str(value)))
        if not math.isfinite(number) or number <= 0:
            raise ValueError("fps must be finite and positive")
        return fps_fraction(number)
    except (TypeError, ZeroDivisionError, OverflowError) as exc:
        raise ValueError("invalid fps") from exc


def probe_asset(path):
    """Count decoded video frames; never infer source length from duration."""
    result = subprocess.run(

View on GitHub (pinned to 8321021c54)