{"record":{"id":"eddca7c98a91135a","repo":"affaan-m/ECC","slug":"reference-source-mutated-during-media-probing","errorCode":null,"errorMessage":"reference source mutated during media probing","messagePattern":"reference source mutated during media probing","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/tasteforge/workflow.py","lineNumber":109,"sourceCode":"                os.close(snapshot_fd)\n            source_digest = digest.hexdigest()\n            copied = os.fstat(descriptor)\n            if (before.st_dev, before.st_ino, before.st_size, before.st_mtime_ns) != (\n                copied.st_dev, copied.st_ino, copied.st_size, copied.st_mtime_ns\n            ):\n                raise ValueError(\"reference source mutated while creating stable snapshot\")\n            verified_size, verified_digest = _hash_descriptor(descriptor)\n            if verified_size != total or verified_digest != source_digest:\n                raise ValueError(\"reference source mutated while creating stable snapshot\")\n\n            measured = probe(snapshot)\n\n            after = os.fstat(descriptor)\n            final_size, final_digest = _hash_descriptor(descriptor)\n            if (before.st_dev, before.st_ino, before.st_size, before.st_mtime_ns) != (\n                after.st_dev, after.st_ino, after.st_size, after.st_mtime_ns\n            ) or final_size != total or final_digest != source_digest:\n                raise ValueError(\"reference source mutated during media probing\")\n            rebound = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)\n            try:\n                rebound_stat = os.fstat(rebound)\n                if (rebound_stat.st_dev, rebound_stat.st_ino) != (before.st_dev, before.st_ino):\n                    raise ValueError(\"reference source identity changed during media probing\")\n            finally:\n                os.close(rebound)\n            return measured, total, source_digest\n    finally:\n        os.close(descriptor)\n\n\ndef _finite_real(value: Any) -> bool:\n    return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value)\n\n\ndef _validate_probe(measured: dict[str, Any]) -> float:\n    def require_finite_evidence(value: Any) -> None:","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/tasteforge/workflow.py#L91-L127","documentation":"TasteForge hashes each reference media file, snapshots it, runs the media probe on the snapshot, then re-hashes the original file to bind the recorded SHA-256 to the exact bytes probed. This ValueError is raised when the post-probe re-check shows the source file's size, mtime, inode metadata, or content digest changed while probing was running. It is a fail-closed integrity guard: the run aborts instead of emitting provenance that does not match the real file.","triggerScenarios":"run_workflow() -> _stable_probe(path, probe) on a reference file that is modified, appended to, truncated, or re-saved by another process (or by the user's own editor/sync tool) between the initial snapshot and the final fstat/hash re-check.","commonSituations":"Cloud sync clients (Dropbox/Drive/OneDrive) rewriting files mid-run; a video editor or download manager still writing the reference file; a build step or asset pipeline that touches/copies references concurrently; cron jobs regenerating assets while the workflow runs.","solutions":["Wait until the reference file is fully written (e.g. finish the download/export, ensure the writer process has closed the file), then re-run run_workflow.","Exclude the reference directory from active cloud-sync/indexer activity, or pause sync while the workflow runs.","Copy references into a stable, workflow-owned input directory and point the config at those copies.","Re-run the workflow; this is a transient concurrency condition, and a retry on an untouched file will pass.","If the mutation is intentional, regenerate downstream artifacts that consumed the old digest so provenance and file bytes stay consistent."],"exampleFix":"// before: probing a file that a downloader is still writing\nreferences = [\"assets/clip.mp4\"]  # clip.mp4 still being written by ffmpeg\n// after: wait for the writer to finish before running\nsubprocess.run([\"ffmpeg\", ...], check=True)\nwhile is_still_growing(\"assets/clip.mp4\"):\n    time.sleep(1)  # size stable across two stats\nrun_workflow(\"contract.json\", \"out/\")","handlingStrategy":"retry","validationCode":"import os, time\n\ndef source_is_stable(path, interval=1.0, checks=2):\n    stats = []\n    for _ in range(checks):\n        st = os.stat(path)\n        stats.append((st.st_dev, st.st_ino, st.st_size, st.st_mtime_ns))\n        time.sleep(interval)\n    return len(set(stats)) == 1\n\n# call before run_workflow\nif not source_is_stable(\"assets/clip.mp4\"):\n    raise RuntimeError(\"reference still changing; wait before probing\")","typeGuard":"def is_quiescent_file(path: str) -> bool:\n    import os, time\n    s1, s2 = os.stat(path), None\n    time.sleep(1.0)\n    s2 = os.stat(path)\n    return (s1.st_size, s1.st_mtime_ns, s1.st_ino) == (s2.st_size, s2.st_mtime_ns, s2.st_ino)","tryCatchPattern":"import time\nfrom skills.taste_application.scripts.tasteforge.workflow import run_workflow\n\nfor attempt in range(3):\n    try:\n        return run_workflow(\"contract.json\", \"out/\")\n    except ValueError as e:\n        if \"mutated during media probing\" not in str(e):\n            raise\n        time.sleep(2 ** attempt)  # back off, let writers finish\nraise RuntimeError(\"reference file kept changing; pause writers/sync and retry\")","preventionTips":["Freeze reference media into a dedicated input directory before running the workflow","Pause cloud-sync clients (Dropbox/Drive) over asset directories during runs","Wait for downloads/exports to fully complete (stable size across two stats) before probing","Run the workflow off the live asset tree; copy, then probe the copy"],"tags":["file-integrity","race-condition","concurrency","media-processing"],"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"}