{"record":{"id":"1e79194f64d0218d","repo":"affaan-m/ECC","slug":"target-overlay-tracks-must-be-empty","errorCode":null,"errorMessage":"target overlay tracks must be empty","messagePattern":"target overlay tracks must be empty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/resolve.py","lineNumber":267,"sourceCode":"    \"\"\"\n    if source_end_mode not in (\"inclusive\", \"exclusive\"):\n        raise ValueError(\"source_end_mode must be inclusive or exclusive\")\n    if not isinstance(source_timeline, str) or not source_timeline:\n        raise ValueError(\"source_timeline must be explicit\")\n    name = timeline.GetName()\n    if not name or name == source_timeline:\n        raise ValueError(\"target must be a distinct versioned timeline\")\n    plan = allocate_placements(\n        placements, fps=fps, base_track_count=base_track_count, probe=probe\n    )\n    if _fps(timeline.GetSetting(\"timelineFrameRate\")) != _fps(fps):\n        raise ValueError(\"target timeline fps mismatch\")\n    count = timeline.GetTrackCount(\"video\")\n    if count < base_track_count:\n        raise ValueError(\"base_track_count exceeds target video tracks\")\n    for track in range(base_track_count + 1, count + 1):\n        if _items(timeline, \"video\", track):\n            raise ValueError(\"target overlay tracks must be empty\")\n    before = _base_snapshot(timeline, base_track_count)\n    for _ in range(count, max(event[\"track\"] for event in plan)):\n        old_count = timeline.GetTrackCount(\"video\")\n        if (\n            not timeline.AddTrack(\"video\")\n            or timeline.GetTrackCount(\"video\") != old_count + 1\n        ):\n            raise RuntimeError(\"could not create overlay track\")\n    appended = []\n    for event in plan:\n        imported = media_pool.ImportMedia([event[\"asset\"]])\n        if not imported or len(imported) != 1:\n            raise RuntimeError(f\"could not import {event['asset']}\")\n        items = media_pool.AppendToTimeline(\n            [\n                {\n                    \"mediaPoolItem\": imported[0],\n                    \"startFrame\": 0,","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/resolve.py#L249-L285","documentation":"apply_placements requires every video track above base_track_count that already exists on the target to be empty, so appended overlays are the only items on those tracks. If any such overlay track already contains clips, it raises before mutating, preventing silent stacking of overlays on leftover content. This is a safety check for the 'versioned duplicate timeline' workflow.","triggerScenarios":"Calling apply_placements on a duplicated timeline where users had already placed clips on upper video tracks (e.g. graphics or text on V2/V3), or re-running apply_placements a second time against the same target after the first run already appended overlays.","commonSituations":"Re-running the tool after a partial failure (retry against a dirty target); duplicating a timeline that includes title/graphics layers; forgetting to clean overlay tracks between runs.","solutions":["Delete or move any clips on video tracks above base_track_count on the target before calling apply_placements.","Create a fresh duplicate of the source timeline for each run so overlay tracks start empty.","Check tracks yourself first with GetItemListInTrack('video', i) and abort/clean if non-empty."],"exampleFix":"// before\napply_placements(target, pool, placements, ...)  # second run\n// after\nfor i in range(base+1, target.GetTrackCount(\"video\")+1):\n    for item in target.GetItemListInTrack(\"video\", i):\n        item.DeleteClip()\napply_placements(target, pool, placements, ...)","handlingStrategy":"validation","validationCode":"for track in range(base_track_count + 1, timeline.GetTrackCount(\"video\") + 1):\n    if timeline.GetItemListInTrack(\"video\", track):\n        raise RuntimeError(f\"overlay track V{track} is not empty\")","typeGuard":"def overlay_tracks_empty(timeline, base_track_count):\n    return all(\n        not timeline.GetItemListInTrack(\"video\", t)\n        for t in range(base_track_count + 1, timeline.GetTrackCount(\"video\") + 1)\n    )","tryCatchPattern":"try:\n    apply_placements(tl, pool, placements, ...)\nexcept ValueError as e:\n    if \"overlay tracks must be empty\" in str(e):\n        target = src.CreateDuplicateTimeline(f\"{name}-clean\")\n        apply_placements(target, pool, placements, ...)\n    else:\n        raise","preventionTips":["Use a fresh duplicate timeline per run; never re-run against a used target.","Clean up after failed runs or roll back to the duplicate instead of retrying in place.","Script a pre-check that deletes stray clips above base tracks before applying."],"tags":["resolve","tracks","validation","timeline"],"backgroundTag":"invalid-state-transition","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"}