affaan-m/ECC · error · ValueError

native clip snapshot required

Error message

native clip snapshot required

What it means

_snapshot requires snapshot.timeline_readback to be a non-empty dict mapping track names to clip lists; this is the native clip evidence the whole preservation workflow binds against. Missing, empty, or wrong-typed readback means there is no original clip data to preserve, so the library refuses to build a bundle.

Solutions

  1. Re-export the snapshot from a timeline that actually contains clips so timeline_readback is populated.
  2. Add the timeline_readback object with keys like "video1", "audio1" mapping to clip lists.
  3. Check the export wasn't truncated — validate the JSON is complete and non-empty.
  4. Verify you're pointing at the snapshot for the intended timeline, not an empty placeholder.

Example fix

// before
{"project": "P", "timeline": "T", "timeline_readback": {}}

// after
{"project": "P", "timeline": "T", "timeline_readback": {"video1": [{"path": "...", "name": "clip", "start": 0, "end": 100, "left_offset": 0, "right_offset": 0, "enabled": true, "properties": {}}], "audio1": [...]}}
Defensive patterns

Strategy: validation

Validate before calling

def has_readback(snapshot):
    tracks = snapshot.get("timeline_readback") if isinstance(snapshot, dict) else None
    return isinstance(tracks, dict) and len(tracks) > 0 and all(isinstance(v, list) for v in tracks.values())

Type guard

def has_timeline_readback(snapshot) -> bool:
    t = snapshot.get("timeline_readback") if isinstance(snapshot, dict) else None
    return isinstance(t, dict) and bool(t)

Try / catch

try:
    bundle = build_application_bundle(config, None, local_only=True)
except ValueError as exc:
    if "native clip snapshot required" in str(exc):
        print("timeline_readback missing/empty — re-export from a timeline with clips")
    raise

Prevention

When it happens

Trigger: build_application_bundle where the snapshot has no timeline_readback key, it is null, an empty object {}, or a list/string instead of a dict of track->clips.

Common situations: An empty or blank timeline exported; a snapshot from a tool that doesn't emit readback; a hand-built snapshot omitting the readback section; a truncated export that dropped the last field.

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

Appendix: source

Thrown at skills/taste-application/scripts/tasteforge/integration.py:216

    # Resolve's conventional decimal NTSC labels represent these exact rates.
    ntsc = {"23.976": "24000/1001", "29.97": "30000/1001", "59.94": "60000/1001"}
    try:
        if type(native_fps) not in (str, int, float):
            raise ValueError("native fps missing")
        label = str(native_fps)
        if not re.fullmatch(r"[0-9]{1,9}(?:\.[0-9]{1,12}|/[1-9][0-9]{0,8})?", label):
            raise ValueError("native fps must use a bounded decimal or rational label")
        native_rate = Fraction(ntsc.get(label, label))
        if native_rate != Fraction(*_rate(baseline["fps"])):
            raise ValueError("native fps differs")
    except (ValueError, ZeroDivisionError) as exc:
        raise ValueError("native snapshot fps is missing, invalid or contradictory") from exc
    for cfg, key in [("project_name", "project"), ("timeline_name", "timeline")]:
        if _text(baseline[cfg]) != snapshot.get(key):
            raise ValueError("native snapshot identity mismatch")
    tracks = snapshot.get("timeline_readback")
    if not isinstance(tracks, dict) or not tracks:
        raise ValueError("native clip snapshot required")
    for track, clips in tracks.items():
        if not re.fullmatch(r"(?:video|audio)[1-9][0-9]*", track) or not isinstance(clips, list):
            raise ValueError("invalid native snapshot track")
        for clip in clips:
            _check_clip(clip, bounds)
    return tracks


def _check_clip(clip: Any, bounds: list[int]) -> None:
    required = {"path", "name", "start", "end", "left_offset", "right_offset", "enabled", "properties"}
    if not isinstance(clip, dict) or not required <= clip.keys():
        raise ValueError("native snapshot clip is incomplete")
    _range([clip["start"], clip["end"]], bounds)
    _integer(clip["left_offset"])
    _integer(clip["right_offset"])
    if clip["path"] is not None:
        _text(clip["path"])
    _text(clip["name"])

View on GitHub (pinned to 8321021c54)