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

  1. Read the timeline's actual rate first: timeline.GetSetting('timelineFrameRate') and pass that value as fps to apply_placements.
  2. Recreate or reconfigure the target timeline so its frame rate matches the source project's fps.
  3. 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

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


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)