{"record":{"id":"30c201c1e6065fbb","repo":"affaan-m/ECC","slug":"placement-event-id-readback-mismatch-actual-r","errorCode":null,"errorMessage":"placement {event['id']} readback mismatch: {actual!r}","messagePattern":"placement (.+?) readback mismatch: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/resolve.py","lineNumber":228,"sourceCode":"        \"duration\": item.GetDuration(),\n        \"enabled\": item.GetClipEnabled(),\n        \"track\": track,\n        \"opacity\": item.GetProperty(\"Opacity\"),\n        \"composite\": item.GetProperty(\"CompositeMode\"),\n        \"path\": _path(item),\n    }\n    expected = {\n        \"start\": event[\"record_frame\"],\n        \"end\": event[\"record_frame\"] + event[\"frames\"],\n        \"duration\": event[\"frames\"],\n        \"enabled\": True,\n        \"track\": track,\n        \"opacity\": event[\"opacity\"],\n        \"composite\": event[\"composite\"],\n        \"path\": event[\"asset\"],\n    }\n    if actual != expected:\n        raise RuntimeError(f\"placement {event['id']} readback mismatch: {actual!r}\")\n    return actual\n\n\ndef apply_placements(\n    timeline,\n    media_pool,\n    placements,\n    *,\n    source_timeline,\n    source_end_mode,\n    fps,\n    base_track_count,\n    probe=probe_asset,\n):\n    \"\"\"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.","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/resolve.py#L210-L246","documentation":"This is tasteforge's post-write verification: the clip's actual start/end/duration/enabled state read back from Resolve must exactly equal the planned values. Any divergence (Resolve adjusted in/out points, disabled the clip, changed duration) throws with the full actual dict so you can see exactly what differed.","triggerScenarios":"Resolve snapping/clamping clip in/out to track or source bounds; fps or frame-rounding differences shifting start/end by a frame; GetClipEnabled returning False; source_end_mode convention wrong so expected end is off by one frame.","commonSituations":"Host endpoint convention (inclusive vs exclusive) not verified for this Resolve version; asset shorter than requested causing auto-trim; timeline frame-rate mismatch silently re-timed the clip; conflicting project-level 'use dynamic trim' settings.","solutions":["Compare the printed actual dict against expected to find the differing field (usually start/end off-by-one)","Re-verify source_end_mode on this Resolve host and pass the correct convention","Check the asset truly contains the requested frames (no auto-trim)","Confirm target timeline fps matches the placement fps before applying"],"exampleFix":"// before\napply_placements(..., source_end_mode=\"inclusive\")  # host is exclusive; end off by 1\n// after\napply_placements(..., source_end_mode=\"exclusive\")  # verified convention for this host","handlingStrategy":"try-catch","validationCode":"expected_frames = end - start\nassert probe(asset).get(\"frames\", 0) >= expected_frames, \"asset would be auto-trimmed\"\nassert _fps(timeline.GetSetting(\"timelineFrameRate\")) == _fps(fps), \"fps mismatch\"","typeGuard":"def readback_matches(actual: dict, expected: dict) -> bool:\n    return all(actual.get(k) == v for k, v in expected.items())","tryCatchPattern":"try:\n    apply_placements(..., source_end_mode=MODE)\nexcept RuntimeError as exc:\n    if \"readback mismatch\" in str(exc):\n        print(exc)  # actual dict shows the off-by-one or disabled field\n    raise","preventionTips":["Verify the host's endpoint convention (inclusive vs exclusive) once and pin it","Match placement fps to the target timeline fps","Ensure assets are long enough so Resolve never auto-trims","Apply to a fresh duplicate timeline to avoid prior-state interference"],"tags":["resolve-api","readback","verification"],"backgroundTag":"checksum-mismatch","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"}