{"record":{"id":"7699e559b68f5190","repo":"affaan-m/ECC","slug":"label-must-be-an-integer-minimum","errorCode":null,"errorMessage":"{label} must be an integer >= {minimum}","messagePattern":"(.+?) must be an integer >= (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/resolve.py","lineNumber":23,"sourceCode":"receipt; partial edits may remain and must be discarded/restored by the caller.\nComposite values are explicit API integers, never guessed blend-mode names.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport copy\nimport json\nimport math\nimport subprocess\nfrom fractions import Fraction\nfrom pathlib import Path\n\nfrom .timeline import fps_fraction\n\n\ndef _integer(value, label, minimum=0):\n    if isinstance(value, bool) or not isinstance(value, int) or value < minimum:\n        raise ValueError(f\"{label} must be an integer >= {minimum}\")\n    return value\n\n\ndef _fps(value):\n    if isinstance(value, bool):\n        raise ValueError(\"fps must be finite and positive\")\n    try:\n        number = float(Fraction(str(value)))\n        if not math.isfinite(number) or number <= 0:\n            raise ValueError(\"fps must be finite and positive\")\n        return fps_fraction(number)\n    except (TypeError, ZeroDivisionError, OverflowError) as exc:\n        raise ValueError(\"invalid fps\") from exc\n\n\ndef probe_asset(path):\n    \"\"\"Count decoded video frames; never infer source length from duration.\"\"\"\n    result = subprocess.run(","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/resolve.py#L5-L41","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Convert with a strict check: int(value) after verifying it is not a float with a fractional part, or round() a computed float","Pass actual ints (e.g. frames=round(duration * fps)) into allocate_placements","Fix upstream config parsing to coerce numeric fields to int before reaching resolve"],"exampleFix":"// before\nallocate_placements(frames=\"12\", ...)  # string\n// after\nallocate_placements(frames=int(\"12\"), ...)  # or max(1, round(duration * fps))","handlingStrategy":"validation","validationCode":"def is_count(v):\n    return isinstance(v, int) and not isinstance(v, bool) and v >= 0\nassert is_count(frames), f\"frames must be a non-negative int, got {frames!r}\"","typeGuard":null,"tryCatchPattern":"try:\n    placements = allocate_placements(frames=frames, ...)\nexcept ValueError as e:\n    frames = max(1, round(float(frames)))\n    placements = allocate_placements(frames=frames, ...)","preventionTips":["Coerce config numbers to int at the config-parsing boundary","Never use bool as a count; guard with isinstance(v, bool) checks","round() any computed float (duration * fps) before passing it"],"tags":["validation","types","integer","resolve"],"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"}