affaan-m/ECC · error · ValueError

asset must be a local regular file

Error message

asset must be a local regular file: {asset}

What it means

After type-checking, allocate_placements expands/resolves the asset path and requires it to exist as a regular file; it raises ValueError('asset must be a local regular file: {asset}') when Path.is_file() is false — the path doesn't exist, is a directory, or is a symlink/special file pointing to nothing regular.

Solutions

  1. Check `os.path.isfile(asset)` before calling and print the resolved path to see where it points
  2. Use absolute paths (or expanduser/resolve yourself) so cwd changes don't break the run
  3. Verify the upstream step that produces the asset succeeded and the file still exists at render time
  4. If it's a directory or URL, point to a regular file instead (e.g. the video file inside the directory)

Example fix

# before
resolve.apply_placements(..., [{'id': 'x', 'asset': 'overlay.webm'}])  # missing file
# after
from pathlib import Path
p = Path('overlay.webm').expanduser().resolve()
if not p.is_file():
    raise FileNotFoundError(f'overlay asset missing: {p}')
resolve.apply_placements(..., [{'id': 'x', 'asset': p}])
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def ensure_assets_exist(events):
    for ev in events:
        a = Path(str(ev.get('asset', ''))).expanduser().resolve()
        if not a.is_file():
            raise FileNotFoundError(f'placement {ev.get("id")!r} asset missing: {a}')

ensure_assets_exist(placements)

Try / catch

try:
    resolve.apply_placements(plan, placements)
except ValueError as e:
    if e.args and e.args[0].startswith('asset must be a local regular file:'):
        missing = e.args[0].rsplit(': ', 1)[1]
        raise AssetError(f'missing overlay asset: {missing}') from e
    raise

Prevention

When it happens

Trigger: Passing a path that was never created, a deleted temp file, a directory path, a broken symlink, a relative path wrong for the current working directory, or a device/pipe special file.

Common situations: Renders run from a different cwd than where relative asset paths were authored; assets generated by an earlier pipeline step that failed silently; assets cleaned up before compositing; using '~/asset.webm' where ~ isn't expanded (though expanduser handles it, the file must exist after resolution).

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

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

            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(
            {
                "id": identifier,
                "asset": key,
                "record_frame": start,

View on GitHub (pinned to 8321021c54)