{"record":{"id":"6fba0d3371ef7fc2","repo":"affaan-m/ECC","slug":"explicit-opacity-must-be-finite-within-0-100","errorCode":null,"errorMessage":"explicit opacity must be finite within 0..100","messagePattern":"explicit opacity must be finite within 0\\.\\.100","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/resolve.py","lineNumber":111,"sourceCode":"    \"\"\"\n    rate = _fps(fps)\n    _integer(base_track_count, \"base_track_count\")\n    checked, seen, metadata = [], set(), {}\n    for event in placements:\n        identifier = event.get(\"id\")\n        if not isinstance(identifier, str) or not identifier or identifier in seen:\n            raise ValueError(\"placement id must be unique and nonempty\")\n        seen.add(identifier)\n        start = _integer(event.get(\"record_frame\"), \"record_frame\")\n        frames = _integer(event.get(\"frames\"), \"frames\", 1)\n        opacity = event.get(\"opacity\")\n        if (\n            isinstance(opacity, bool)\n            or not isinstance(opacity, (int, float))\n            or not math.isfinite(opacity)\n            or not 0 <= opacity <= 100\n        ):\n            raise ValueError(\"explicit opacity must be finite within 0..100\")\n        composite = _integer(event.get(\"composite\"), \"composite\")\n        raw_asset = event.get(\"asset\")\n        if not isinstance(raw_asset, (str, Path)) or not str(raw_asset):\n            raise ValueError(\"asset must be a local regular file\")\n        asset = Path(raw_asset).expanduser().resolve()\n        if not asset.is_file():\n            raise ValueError(f\"asset must be a local regular file: {asset}\")\n        alpha = event.get(\"requires_alpha\", False)\n        if not isinstance(alpha, bool):\n            raise ValueError(\"requires_alpha must be boolean\")\n        key = str(asset)\n        if key not in metadata:\n            metadata[key] = probe(asset)\n        info = metadata[key]\n        if _fps(info.get(\"fps\")) != rate:\n            raise ValueError(f\"asset fps differs from timeline: {asset}\")\n        if _integer(info.get(\"frames\"), \"asset frames\", 1) < frames:\n            raise ValueError(f\"asset contains too few frames: {asset}\")","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/resolve.py#L93-L129","documentation":"When a placement event includes an 'opacity' key, allocate_placements requires it to be a non-bool int/float that is finite (not NaN/inf) and within 0..100; otherwise it raises ValueError('explicit opacity must be finite within 0..100'). Strings like '50' are rejected — opacity must be numeric.","triggerScenarios":"Passing opacity='50' (string), opacity=True/False, opacity=None, NaN or float('inf') (e.g. from a bad JSON config or a division by zero upstream), or opacity=150.","commonSituations":"Config where opacity was entered as a percentage string; JSON parser can't produce NaN but Python float('nan') from upstream math can; a script computing opacity by division that yielded inf/nan; leaving opacity as a boolean toggle.","solutions":["Supply opacity as a plain number between 0 and 100 (e.g. 50, 87.5), not a string or bool","Validate/convert at the boundary: `o = float(raw); assert 0 <= o <= 100 and math.isfinite(o)`","Fix upstream math that produced NaN/inf (division by zero, log of negative) before assigning opacity","Omit the 'opacity' key entirely if you want the default rather than passing None"],"exampleFix":"// before\n{\"id\": \"logo\", \"opacity\": \"50\"}\n// after\n{\"id\": \"logo\", \"opacity\": 50}","handlingStrategy":"validation","validationCode":"import math\n\ndef valid_opacity(v):\n    return (not isinstance(v, bool)\n            and isinstance(v, (int, float))\n            and math.isfinite(v)\n            and 0 <= v <= 100)\n\nassert valid_opacity(50)","typeGuard":"def is_opacity(v) -> bool:\n    return isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(v) and 0 <= v <= 100","tryCatchPattern":"try:\n    resolve.allocate_placements(fps, count, placements)\nexcept ValueError as e:\n    if 'opacity' in str(e):\n        raise PlacementError(f'bad opacity value in placements') from e\n    raise","preventionTips":["Keep opacity numeric (0–100) in configs, not strings","Clamp computed opacity: `min(100.0, max(0.0, value))` and check isfinite","Omit the key instead of passing None for defaults"],"tags":["validation","opacity","type-mismatch"],"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"}