{"record":{"id":"ef3f7b2e1b9a779b","repo":"affaan-m/ECC","slug":"protected-intervals-must-be-explicit-and-nonempty","errorCode":null,"errorMessage":"protected intervals must be explicit and nonempty","messagePattern":"protected intervals must be explicit and nonempty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/integration.py","lineNumber":267,"sourceCode":"    if item[\"media\"][\"path\"] != clip[\"path\"]:\n        raise ValueError(\"source path does not match native clip\")\n    if _rate(item[\"fps\"]) != _rate(baseline[\"fps\"]):\n        raise ValueError(\"source fps/retime ambiguity\")\n    duration = clip[\"end\"] - clip[\"start\"]\n    capacity = _integer(item[\"media_frames\"], 1)\n    if capacity != clip[\"left_offset\"] + duration + clip[\"right_offset\"]:\n        raise ValueError(\"source capacity does not match native offsets\")\n    source = _range(item[\"source_range\"], [clip[\"left_offset\"], clip[\"left_offset\"] + duration])\n    target = _range(item[\"timeline_range\"], [clip[\"start\"], clip[\"end\"]])\n    mapped = [clip[\"start\"] + f - clip[\"left_offset\"] for f in source]\n    if mapped != target or (audio and target != [clip[\"start\"], clip[\"end\"]]):\n        raise ValueError(\"source/audio placement must preserve native timing\")\n    return track, index\n\n\ndef _preserved_stack(protected: Any, tracks: dict, bounds: list[int]) -> list[dict]:\n    if not isinstance(protected, list) or not protected:\n        raise ValueError(\"protected intervals must be explicit and nonempty\")\n    for item in protected:\n        _object(item, {\"range\", \"reason\"})\n        _range(item[\"range\"], bounds)\n        _text(item[\"reason\"])\n        if not any(_overlap(item[\"range\"], [c[\"start\"], c[\"end\"]])\n                   for key, clips in tracks.items() if key.startswith(\"video\") for c in clips):\n            raise ValueError(\"protected interval has no original video stack\")\n    return [{\"track\": track, \"clip_index\": index, \"clip\": copy.deepcopy(clip),\n             \"clip_sha256\": _digest(clip)}\n            for track, clips in sorted(tracks.items()) for index, clip in enumerate(clips)\n            if any(_overlap(p[\"range\"], [clip[\"start\"], clip[\"end\"]]) for p in protected)]\n\n\ndef _candidates(items: list, source_hash: str, input_hash: str, source_url: str) -> dict:\n    result = {}\n    for item in items:\n        _object(item, {\"id\", \"media\", \"media_frames\", \"fps\", \"origin\", \"relationship\",\n                       \"source_sha256\", \"compiled_input_sha256\", \"review_status\", \"generation_receipt\"})","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/integration.py#L249-L285","documentation":"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.","triggerScenarios":"Calling build_application_bundle with protected=None, protected=[], or protected as a dict/string instead of a list of {range, reason} objects.","commonSituations":"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.","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."],"exampleFix":"// before\nbuild_application_bundle(bundle, protected=None)\n// after\nbuild_application_bundle(bundle, protected=[{'range': [0, 200], 'reason': 'locked intro'}])","handlingStrategy":"validation","validationCode":"assert isinstance(protected, list) and protected, \\\n    'protected intervals must be a nonempty list of {range, reason} dicts'\nfor p in protected:\n    assert isinstance(p, dict) and 'range' in p and 'reason' in p\n    assert isinstance(p['reason'], str) and p['reason']","typeGuard":"def is_valid_protection(protected) -> bool:\n    return (\n        isinstance(protected, list) and bool(protected)\n        and all(\n            isinstance(p, dict)\n            and isinstance(p.get('range'), list)\n            and isinstance(p.get('reason'), str) and p['reason']\n            for p in protected\n        )\n    )","tryCatchPattern":"try:\n    bundle = build_application_bundle(...)\nexcept ValueError as e:\n    if 'protected intervals must be explicit and nonempty' in str(e):\n        log.error('protected argument missing or empty', protected=protected)\n    raise","preventionTips":["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"],"tags":["validation","arguments","timeline"],"backgroundTag":"empty-required-field","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}