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
- Pass a nonempty list of protection items, each a dict with keys 'range' and 'reason'.
- Ensure every item's 'range' is a valid frame range within timeline bounds and 'reason' is a nonempty string (further checks follow this raise).
- 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
- Treat protected intervals as a required argument, never optional/None
- Declare explicit protection items with meaningful reasons
- Validate protection payloads before each build_application_bundle call
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
- at least one non-empty
- base_track_count exceeds target video tracks
- clip has no 'path
- clip has no 'path
- clip ( ) has non-positive duration
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)