affaan-m/ECC · error · ValueError

asset must be a local regular file

Error message

asset must be a local regular file

What it means

allocate_placements requires each event's 'asset' to be a str or pathlib.Path whose string form is nonempty; otherwise it raises ValueError('asset must be a local regular file'). This generic message fires on missing/empty/wrong-typed asset values, before the path is even resolved.

Solutions

  1. Ensure every event has 'asset' set to a nonempty string or pathlib.Path pointing to a local file
  2. Fix key typos so the path lands under 'asset'
  3. Download remote assets to disk first; this API works only with local files
  4. Pre-validate event dicts with a schema (e.g. pydantic/zod-like check: asset: str, min_length 1) before calling

Example fix

// before
{"id": "bug", "asset": ""}
// after
{"id": "bug", "asset": "/assets/ladybug.webm"}
Defensive patterns

Strategy: validation

Validate before calling

def validate_assets(events):
    for ev in events:
        a = ev.get('asset')
        if not isinstance(a, (str, __import__('pathlib').Path)) or not str(a):
            raise ValueError(f'placement {ev.get("id")!r} has no asset path')

validate_assets(placements)

Type guard

from pathlib import Path

def has_asset(ev) -> bool:
    a = ev.get('asset')
    return isinstance(a, (str, Path)) and bool(str(a))

Try / catch

try:
    resolve.plan(placements, fps=fps)
except ValueError as e:
    if 'asset must be a local regular file' in str(e):
        raise PlacementError('a placement is missing its asset field') from e
    raise

Prevention

When it happens

Trigger: An event dict missing 'asset', asset=None, asset='' , or asset being something else entirely (an int id, a URL string is actually accepted as a str here but will fail the next is_file() check — the type failure raises this one).

Common situations: Templated configs where a placeholder wasn't filled in; renamed key ('file' instead of 'asset'); passing a remote URL expecting the library to download it (it requires local files); passing a directory instead of a file path (type ok, but see the path-specific variant).

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

    for event in placements:
        identifier = event.get("id")
        if not isinstance(identifier, str) or not identifier or identifier in seen:
            raise ValueError("placement id must be unique and nonempty")
        seen.add(identifier)
        start = _integer(event.get("record_frame"), "record_frame")
        frames = _integer(event.get("frames"), "frames", 1)
        opacity = event.get("opacity")
        if (
            isinstance(opacity, bool)
            or not isinstance(opacity, (int, float))
            or not math.isfinite(opacity)
            or not 0 <= opacity <= 100
        ):
            raise ValueError("explicit opacity must be finite within 0..100")
        composite = _integer(event.get("composite"), "composite")
        raw_asset = event.get("asset")
        if not isinstance(raw_asset, (str, Path)) or not str(raw_asset):
            raise ValueError("asset must be a local regular file")
        asset = Path(raw_asset).expanduser().resolve()
        if not asset.is_file():
            raise ValueError(f"asset must be a local regular file: {asset}")
        alpha = event.get("requires_alpha", False)
        if not isinstance(alpha, bool):
            raise ValueError("requires_alpha must be boolean")
        key = str(asset)
        if key not in metadata:
            metadata[key] = probe(asset)
        info = metadata[key]
        if _fps(info.get("fps")) != rate:
            raise ValueError(f"asset fps differs from timeline: {asset}")
        if _integer(info.get("frames"), "asset frames", 1) < frames:
            raise ValueError(f"asset contains too few frames: {asset}")
        if alpha and info.get("has_alpha") is not True:
            raise ValueError(f"asset requires verified alpha: {asset}")
        checked.append(
            {

View on GitHub (pinned to 8321021c54)