{"record":{"id":"d1b078e17754eb0c","repo":"affaan-m/ECC","slug":"target-timeline-fps-mismatch","errorCode":null,"errorMessage":"target timeline fps mismatch","messagePattern":"target timeline fps mismatch","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/resolve.py","lineNumber":261,"sourceCode":"    \"\"\"Append and verify overlays; returns an in-memory placement receipt only.\n\n    This does not save/export/render a project. Supply the selected target's\n    media pool. Existing overlay tracks must be empty; base tracks are preserved.\n    source_end_mode is required: use the endpoint convention verified on this\n    Resolve host. No automatic retry occurs if that convention is incorrect.\n    \"\"\"\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:","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/resolve.py#L243-L279","documentation":"apply_placements enforces that the fps you pass exactly matches the target DaVinci Resolve timeline's timelineFrameRate setting, compared via exact Fraction conversion (_fps). If they differ, the function raises before mutating anything, because appending clips at a wrong frame rate would shift record frames and durations. This is a pre-mutation guard so the timeline stays untouched.","triggerScenarios":"Calling apply_placements with fps=25.0 while the target timeline's timelineFrameRate is 24 (or 23.976, or an NTSC-decimal that snaps differently), or passing an fps object whose Fraction form is not exactly equal to the timeline's setting after NTSC snapping.","commonSituations":"Creating a 'versioned' target timeline manually in Resolve with a different preset frame rate than the source; hardcoding fps=30 in a script against a project configured for 29.97; copying a timeline into a project with a different default frame rate.","solutions":["Read the timeline's actual rate first: timeline.GetSetting('timelineFrameRate') and pass that value as fps to apply_placements.","Recreate or reconfigure the target timeline so its frame rate matches the source project's fps.","Pass an exact Fraction for NTSC rates (e.g. Fraction(30000, 1001)) instead of a rounded decimal like 29.97 so _fps snapping matches the host."],"exampleFix":"// before\napply_placements(tl, pool, placements, fps=30, ...)\n// after\nfps = tl.GetSetting(\"timelineFrameRate\")\napply_placements(tl, pool, placements, fps=float(fps), ...)","handlingStrategy":"validation","validationCode":"tl_fps = float(timeline.GetSetting(\"timelineFrameRate\"))\nif fps_fraction(tl_fps) != fps_fraction(fps):\n    raise ValueError(f\"target fps {tl_fps} != requested {fps}\")","typeGuard":"def has_valid_fps(timeline):\n    raw = timeline.GetSetting(\"timelineFrameRate\")\n    try:\n        return raw is not None and float(raw) > 0\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    apply_placements(tl, pool, placements, fps=fps, ...)\nexcept ValueError as e:\n    if \"fps mismatch\" in str(e):\n        fps = float(tl.GetSetting(\"timelineFrameRate\"))\n        apply_placements(tl, pool, placements, fps=fps, ...)\n    else:\n        raise","preventionTips":["Always derive fps from timeline.GetSetting('timelineFrameRate'), never hardcode.","Use fps_fraction for all fps comparisons so NTSC snapping is consistent.","Create target timelines from a duplicate of the source so frame rate matches by construction."],"tags":["resolve","fps","validation","timeline"],"backgroundTag":"invalid-config-value","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"}