affaan-m/ECC · error · ValueError
target timeline fps mismatch
Error message
target timeline fps mismatch
What it means
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.
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.
Example fix
// before
apply_placements(tl, pool, placements, fps=30, ...)
// after
fps = tl.GetSetting("timelineFrameRate")
apply_placements(tl, pool, placements, fps=float(fps), ...) Defensive patterns
Strategy: validation
Validate before calling
tl_fps = float(timeline.GetSetting("timelineFrameRate"))
if fps_fraction(tl_fps) != fps_fraction(fps):
raise ValueError(f"target fps {tl_fps} != requested {fps}") Type guard
def has_valid_fps(timeline):
raw = timeline.GetSetting("timelineFrameRate")
try:
return raw is not None and float(raw) > 0
except (TypeError, ValueError):
return False Try / catch
try:
apply_placements(tl, pool, placements, fps=fps, ...)
except ValueError as e:
if "fps mismatch" in str(e):
fps = float(tl.GetSetting("timelineFrameRate"))
apply_placements(tl, pool, placements, fps=fps, ...)
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- base_track_count exceeds target video tracks
- fps must be finite and positive
- fps must be positive, got
- fps must be positive, got
- target overlay tracks must be empty
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/d1b078e17754eb0c.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/resolve.py:261
"""Append and verify overlays; returns an in-memory placement receipt only.
This does not save/export/render a project. Supply the selected target's
media pool. Existing overlay tracks must be empty; base tracks are preserved.
source_end_mode is required: use the endpoint convention verified on this
Resolve host. No automatic retry occurs if that convention is incorrect.
"""
if source_end_mode not in ("inclusive", "exclusive"):
raise ValueError("source_end_mode must be inclusive or exclusive")
if not isinstance(source_timeline, str) or not source_timeline:
raise ValueError("source_timeline must be explicit")
name = timeline.GetName()
if not name or name == source_timeline:
raise ValueError("target must be a distinct versioned timeline")
plan = allocate_placements(
placements, fps=fps, base_track_count=base_track_count, probe=probe
)
if _fps(timeline.GetSetting("timelineFrameRate")) != _fps(fps):
raise ValueError("target timeline fps mismatch")
count = timeline.GetTrackCount("video")
if count < base_track_count:
raise ValueError("base_track_count exceeds target video tracks")
for track in range(base_track_count + 1, count + 1):
if _items(timeline, "video", track):
raise ValueError("target overlay tracks must be empty")
before = _base_snapshot(timeline, base_track_count)
for _ in range(count, max(event["track"] for event in plan)):
old_count = timeline.GetTrackCount("video")
if (
not timeline.AddTrack("video")
or timeline.GetTrackCount("video") != old_count + 1
):
raise RuntimeError("could not create overlay track")
appended = []
for event in plan:
imported = media_pool.ImportMedia([event["asset"]])
if not imported or len(imported) != 1:View on GitHub (pinned to 8321021c54)