affaan-m/ECC · error · ValueError

protected intervals must be explicit and nonempty

Error message

protected intervals must be explicit and nonempty

What it means

Raised by _preserved_stack when the protected/preserved intervals argument is not a nonempty list. The library requires callers to explicitly declare which timeline intervals must be untouched; an absent or empty list is treated as an ambiguous, unsafe request.

Solutions

  1. Pass a nonempty list of protection items, each a dict with keys 'range' and 'reason'.
  2. Ensure every item's 'range' is a valid frame range within timeline bounds and 'reason' is a nonempty string (further checks follow this raise).
  3. If nothing needs preservation, compute and pass the intervals you intend to allow editing, per the library's explicit-protection contract, rather than omitting the argument.

Example fix

// before
build_application_bundle(bundle, protected=None)
// after
build_application_bundle(bundle, protected=[{'range': [0, 200], 'reason': 'locked intro'}])
Defensive patterns

Strategy: validation

Validate before calling

assert isinstance(protected, list) and protected, \
    'protected intervals must be a nonempty list of {range, reason} dicts'
for p in protected:
    assert isinstance(p, dict) and 'range' in p and 'reason' in p
    assert isinstance(p['reason'], str) and p['reason']

Type guard

def is_valid_protection(protected) -> bool:
    return (
        isinstance(protected, list) and bool(protected)
        and all(
            isinstance(p, dict)
            and isinstance(p.get('range'), list)
            and isinstance(p.get('reason'), str) and p['reason']
            for p in protected
        )
    )

Try / catch

try:
    bundle = build_application_bundle(...)
except ValueError as e:
    if 'protected intervals must be explicit and nonempty' in str(e):
        log.error('protected argument missing or empty', protected=protected)
    raise

Prevention

When it happens

Trigger: Calling build_application_bundle with protected=None, protected=[], or protected as a dict/string instead of a list of {range, reason} objects.

Common situations: Callers omitting the field when there is nothing to preserve instead of listing explicit intervals; config keys left at default None; mistaking 'no protected intervals' for an empty list rather than the full-interval declaration the API expects.

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

Appendix: source

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

    if item["media"]["path"] != clip["path"]:
        raise ValueError("source path does not match native clip")
    if _rate(item["fps"]) != _rate(baseline["fps"]):
        raise ValueError("source fps/retime ambiguity")
    duration = clip["end"] - clip["start"]
    capacity = _integer(item["media_frames"], 1)
    if capacity != clip["left_offset"] + duration + clip["right_offset"]:
        raise ValueError("source capacity does not match native offsets")
    source = _range(item["source_range"], [clip["left_offset"], clip["left_offset"] + duration])
    target = _range(item["timeline_range"], [clip["start"], clip["end"]])
    mapped = [clip["start"] + f - clip["left_offset"] for f in source]
    if mapped != target or (audio and target != [clip["start"], clip["end"]]):
        raise ValueError("source/audio placement must preserve native timing")
    return track, index


def _preserved_stack(protected: Any, tracks: dict, bounds: list[int]) -> list[dict]:
    if not isinstance(protected, list) or not protected:
        raise ValueError("protected intervals must be explicit and nonempty")
    for item in protected:
        _object(item, {"range", "reason"})
        _range(item["range"], bounds)
        _text(item["reason"])
        if not any(_overlap(item["range"], [c["start"], c["end"]])
                   for key, clips in tracks.items() if key.startswith("video") for c in clips):
            raise ValueError("protected interval has no original video stack")
    return [{"track": track, "clip_index": index, "clip": copy.deepcopy(clip),
             "clip_sha256": _digest(clip)}
            for track, clips in sorted(tracks.items()) for index, clip in enumerate(clips)
            if any(_overlap(p["range"], [clip["start"], clip["end"]]) for p in protected)]


def _candidates(items: list, source_hash: str, input_hash: str, source_url: str) -> dict:
    result = {}
    for item in items:
        _object(item, {"id", "media", "media_frames", "fps", "origin", "relationship",
                       "source_sha256", "compiled_input_sha256", "review_status", "generation_receipt"})

View on GitHub (pinned to 8321021c54)