{"record":{"id":"1f68125996e5ad41","repo":"affaan-m/ECC","slug":"fmt-export-did-not-create-a-timeline-tp","errorCode":null,"errorMessage":"{fmt} export did not create a timeline: {tp}","messagePattern":"(.+?) export did not create a timeline: (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/forge.py","lineNumber":287,"sourceCode":"    # timeline that can be re-cut, re-ordered and re-graded rather than as a\n    # single clip somebody has to razor by hand.\n    #\n    # Shot files are kept: the timeline references them by absolute path, so\n    # deleting work_dir breaks the handoff even though the mp4 still plays.\n    tl_clips = [\n        {\"path\": str(Path(c).resolve()),\n         \"duration\": frame_mod.probe(c).duration,\n         \"name\": Path(c).stem}\n        for c in order\n    ]\n    timelines = {}\n    for fmt in (\"fcpxml\", \"edl\"):\n        tp = tl_mod.write_timeline(\n            tl_clips, fps=FPS, out_path=out_path.with_suffix(\".\" + fmt),\n            fmt=fmt, title=f\"{genre}_cut\", width=W, height=H,\n        )\n        if not Path(tp).is_file():\n            raise RuntimeError(f\"{fmt} export did not create a timeline: {tp}\")\n        timelines[fmt] = str(tp)\n        print(f\"  timeline -> {tp}\")\n\n    asm.write_manifest(out_path.with_suffix(\".json\"), {\n        \"genre\": genre,\n        \"work_directory\": str(work_dir),\n        \"created\": datetime.now(timezone.utc).strftime(\"%Y-%m-%dT%H:%M:%SZ\"),\n        \"geometry\": {\"width\": W, \"height\": H, \"fps\": FPS},\n        \"takes\": [str(t) for t in takes],\n        \"base_video\": base_video,\n        \"base_ratio\": base_ratio if base_shots else 0.0,\n        \"overlays\": ov,\n        \"shots\": len(order),\n        \"generated_shots\": len(gen_shots),\n        \"base_shots\": len(base_shots),\n        \"duration\": round(final.duration, 3),\n        \"duration_contract\": duration_contract,\n        \"grade_strength\": strength,","sourceCodeStart":269,"sourceCodeEnd":305,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/forge.py#L269-L305","documentation":"forge.py raises RuntimeError after writing each timeline export (fcpxml and edl) when the path returned by write_timeline does not exist on disk. It is a post-condition check that the exporter both returned a truthy path and actually created the file; if either silently failed the export is declared broken.","triggerScenarios":"write_timeline returns a path string that does not point to a created file - typically because the writer failed silently, the output directory was deleted mid-run, or the path is unwritable due to permissions.","commonSituations":"Running forge in a read-only work directory, a custom write_timeline override returning the intended path without writing, disk-full conditions where the open() for the export fails but the path is still returned, or out_path parent directories that do not exist.","solutions":["Check that the parent directory of out_path exists and is writable before running forge","Verify disk space is available; a full disk makes file writes fail","Inspect write_timeline in tl_mod to confirm it actually writes the file and raises on failure rather than returning a path unconditionally","Run with the same out_path in a fresh directory to rule out stale permission or symlink issues"],"exampleFix":"// before\nout_path = work_dir / \"cut\"\nfor fmt in (\"fcpxml\", \"edl\"):\n    tp = tl_mod.write_timeline(...)\n// after\nout_path = work_dir / \"cut\"\nout_path.parent.mkdir(parents=True, exist_ok=True)\nfor fmt in (\"fcpxml\", \"edl\"):\n    tp = tl_mod.write_timeline(...)","handlingStrategy":"validation","validationCode":"from pathlib import Path\nif not Path(out_path).parent.exists() or not Path(out_path).parent.stat().st_mode & 0o200:\n    raise SystemExit(f\"output directory not writable: {out_path.parent}\")","typeGuard":"def is_writable_dir(p) -> bool:\n    from pathlib import Path\n    p = Path(p)\n    return p.is_dir() and p.stat().st_mode & 0o200 != 0","tryCatchPattern":"try:\n    forge(...)\nexcept RuntimeError as e:\n    print(f\"timeline export failed: {e}\"); check_disk_and_perms(out_path)","preventionTips":["mkdir the output parent before running forge","Monitor free disk space before long export runs","Never delete the work directory while forge is running"],"tags":["file-io","export","postcondition"],"backgroundTag":"file-write-failed","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"}