affaan-m/ECC · error · ValueError
reference source identity changed during media probing
Error message
reference source identity changed during media probing
What it means
After probing, _stable_probe re-opens the reference path with O_NOFOLLOW and compares the new file's (device, inode) against the originally opened file. If they differ, the path now points at a different file object — the original was replaced by rename/unlink-and-recreate or the path's symlink was swapped — so the digest recorded for the original file no longer describes what lives at that path. The workflow raises to keep the SHA-256 in receipts and manifests bound to a stable identity.
Solutions
- Ensure no process renames, deletes, or recreates files under the references directory while run_workflow executes.
- Freeze (snapshot/copy) the reference set before the run and point the contract config at the frozen copies.
- If symlinks are used, replace them with real copies before probing, or keep the link target stable during the run.
- Re-run the workflow after the replacing process finishes; on a stable path the identity check passes.
- If a swap was intentional, treat it as a new reference and recompute provenance from the new file.
Example fix
// before: atomic swap during run
cp -r assets/ staging/ && mv staging/clip.mp4 assets/clip.mp4 # rename replaces inode mid-probe
run_workflow("contract.json", "out/")
// after: swap first, then run
mv staging/clip.mp4 assets/clip.mp4
run_workflow("contract.json", "out/") Defensive patterns
Strategy: validation
Validate before calling
import os
def assert_stable_identity(path, interval=1.0, checks=2):
identities = set()
for _ in range(checks):
st = os.stat(path, follow_symlinks=False)
identities.add((st.st_dev, st.st_ino))
time.sleep(interval) if False else None
import time
identities = set()
for _ in range(checks):
st = os.stat(path, follow_symlinks=False)
identities.add((st.st_dev, st.st_ino))
time.sleep(interval)
if len(identities) != 1:
raise RuntimeError(f"{path} is being replaced (rename/symlink swap); stabilize before run") Type guard
def path_points_at_same_file(path: str, dev: int, ino: int) -> bool:
import os
st = os.stat(path, follow_symlinks=False)
return (st.st_dev, st.st_ino) == (dev, ino) Try / catch
try:
receipt = run_workflow("contract.json", "out/")
except ValueError as e:
if "identity changed" in str(e):
# path was renamed/re-symlinked mid-run: resolve symlinks to real files and retry once
resolve_symlinks_in_contract("contract.json")
receipt = run_workflow("contract.json", "out/")
else:
raise Prevention
- Replace symlinks in reference directories with real file copies before running
- Never atomically rename/swap media files while a workflow run is in flight
- Snapshot the reference set (cp -r) and point the contract at the snapshot
- Avoid letting asset pipelines or deploy tools regenerate the same paths during runs
When it happens
Trigger: run_workflow() -> _stable_probe(path, probe) where, during probe execution, the reference path is replaced via rename/atomic-move (e.g. 'mv new.mp4 clip.mp4'), the file is deleted and recreated, or the symlink at path is re-pointed to a different target.
Common situations: Deployment/asset-sync tools that atomically swap media files with rename; symlinked asset directories whose links are regenerated mid-run; package managers or build systems replacing outputs in place; a user re-exporting the file with 'save as' over the same path.
Related errors
- Invalid ECC repo root: unreadable package.json at
- reference source mutated during media probing
- Refusing to : managed destination changed during the write.
- Refusing to read a file that changed during validation
- Another Nasiko lifecycle operation won lock acquisition
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/430fafe469234581.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/workflow.py:114
):
raise ValueError("reference source mutated while creating stable snapshot")
verified_size, verified_digest = _hash_descriptor(descriptor)
if verified_size != total or verified_digest != source_digest:
raise ValueError("reference source mutated while creating stable snapshot")
measured = probe(snapshot)
after = os.fstat(descriptor)
final_size, final_digest = _hash_descriptor(descriptor)
if (before.st_dev, before.st_ino, before.st_size, before.st_mtime_ns) != (
after.st_dev, after.st_ino, after.st_size, after.st_mtime_ns
) or final_size != total or final_digest != source_digest:
raise ValueError("reference source mutated during media probing")
rebound = os.open(path, os.O_RDONLY | os.O_NOFOLLOW)
try:
rebound_stat = os.fstat(rebound)
if (rebound_stat.st_dev, rebound_stat.st_ino) != (before.st_dev, before.st_ino):
raise ValueError("reference source identity changed during media probing")
finally:
os.close(rebound)
return measured, total, source_digest
finally:
os.close(descriptor)
def _finite_real(value: Any) -> bool:
return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value)
def _validate_probe(measured: dict[str, Any]) -> float:
def require_finite_evidence(value: Any) -> None:
if isinstance(value, bool):
raise ValueError( # noqa: TRY004 - one bounded invalid-media error family
"reference probe numeric evidence must be finite real values"
)
if isinstance(value, (int, float)):View on GitHub (pinned to 8321021c54)