affaan-m/ECC · error · Error
Refusing to read a file that changed during validation
Error message
Refusing to read a file that changed during validation: ${filePath}. What it means
readRegularFileSnapshot re-stats the file after reading and compares inode identity (before, after, and the on-disk path stat) to detect concurrent modification or a path/symlink swap during the read. Any mismatch throws this error so the installer never operates on a file whose contents or identity changed while being validated. It is a TOCTOU protection for install-state and destination files.
Solutions
- Re-run the guided install once no other process is touching the files
- Kill concurrent install/setup processes and pause file-sync clients during install
- Restore the expected file from a backup or delete the changed state file and re-run the preview to regenerate it
Example fix
// before: concurrent process overwrites state during preview runSetup(...) // throws 'changed during validation' // after // stop other writers/sync, then: runSetup(...) // stable read succeeds
Defensive patterns
Strategy: retry
Validate before calling
// Quiesce writers first: check for locks/concurrent processes before reading
if (installLockExists()) throw new Error('Another install is running; wait and retry') Try / catch
try {
return snapshot(filePath)
} catch (err) {
if (err.message.includes('changed during validation')) {
await waitForQuiescence(filePath)
return snapshot(filePath) // retry once after writers stop
}
throw err
} Prevention
- Use an install lock to serialize guided install runs
- Pause file-sync clients (Dropbox/OneDrive) during install
- Run installs outside of periods when editors/formatters touch the target directory
When it happens
Trigger: Another process (editor, sync client, concurrent install run) modifies the target file or swaps it for a symlink/different inode between the initial fstat, the read, and the final lstat of the path.
Common situations: Two guided installs running concurrently; Dropbox/OneDrive sync touching files mid-install; an editor or background formatter rewriting the file while the preview runs.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- artifact byte count exceeded during reading
- artifact changed before reading
- artifact changed during reading
- Cannot create exclusive receipt
- capsule.lock_lost
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/7c17daec1728de76.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/multi-harness-setup.js:99
if (error && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) return null;
throw error;
}
try {
const before = fs.fstatSync(descriptor);
if (!before.isFile()) {
throw new Error(`Refusing to read a non-file at ${filePath}.`);
}
const content = fs.readFileSync(descriptor);
const after = fs.fstatSync(descriptor);
const finalPathStat = fs.lstatSync(filePath);
if (
finalPathStat.isSymbolicLink()
|| !finalPathStat.isFile()
|| !sameFileIdentity(before, after)
|| !sameFileIdentity(after, finalPathStat)
) {
throw new Error(`Refusing to read a file that changed during validation: ${filePath}.`);
}
return { content, stat: after };
} finally {
fs.closeSync(descriptor);
}
}
function fingerprintFile(filePath) {
const snapshot = readRegularFileSnapshot(filePath);
if (!snapshot) return { exists: false, sha256: null };
return {
exists: true,
sha256: crypto.createHash('sha256').update(snapshot.content).digest('hex'),
};
}
function fingerprintInstallStateValue(state) {
const content = Buffer.from(`${JSON.stringify(state, null, 2)}\n`);View on GitHub (pinned to 8321021c54)