affaan-m/ECC · error · Error
Refusing to trust install-state that changed during…
Error message
Refusing to trust install-state that changed during validation: ${plan.installStatePath}. What it means
readOwnedDestinations fingerprints the install-state file (exists flag + sha256) before and after reading and validating it. If the two fingerprints differ, the file changed concurrently while being validated, so its contents cannot be trusted, and this error is thrown. This is a TOCTOU guard preventing a racing process or editor from swapping the state between read and verification.
Solutions
- Re-run the install once no other install/update process is active (ensure single-writer).
- Pause file-sync tools or editors watching the target directory during install.
- Delete the changed state file and re-run the guided install to rebuild a consistent state.
Example fix
// before: two installs racing runInstall(planA) && runInstall(planB); // second mutates state mid-validation // after: serialize installs await runInstall(planA); await runInstall(planB);
Defensive patterns
Strategy: retry
Validate before calling
const before = fingerprintFile(plan.installStatePath);
// ensure no concurrent writer before invoking
if (isInstallRunning()) throw new Error('Another install is in progress'); Type guard
function isStateStable(fp1, fp2) {
return fp1.exists === fp2.exists && fp1.sha256 === fp2.sha256;
} Try / catch
try {
const owned = readOwnedDestinations(plan, deps);
} catch (err) {
if (String(err.message).includes('changed during validation')) {
// wait and retry once with no concurrent processes
await waitForFileQuiet(plan.installStatePath);
return readOwnedDestinations(plan, deps);
}
throw err;
} Prevention
- Run one install at a time; use a lock file for scripted installs.
- Pause Dropbox/rsync/git operations on the target directory during install.
- Avoid editors with auto-save watching the state directory during installs.
- Retry the install once after the error; persistent instability means a background writer exists.
When it happens
Trigger: The install-state file is modified, replaced, or deleted between fingerprintFile() before readState() and the fingerprint taken after — e.g. a concurrent install/update process, an editor auto-save, or a sync tool (Dropbox/rsync) touching the file during install.
Common situations: Running two installs of ECC in parallel; a dotfile-sync service updating ~/.config while an install runs; a test or script regenerating install-state mid-run; VCS checkout/branch switch during install.
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
- Another Nasiko lifecycle operation won lock acquisition
- Another Nasiko lifecycle operation won stale-lock recovery
- capsule.lock_lost
- dispatch transition lost
- Legacy sync path changed before removal
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/dd3536e4cf400242.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/multi-harness-setup.js:194
return { destinations: new Set(), stateFingerprint: { exists: false, sha256: null } };
}
try {
assertSafeInstallOperation(plan, { destinationPath: plan.installStatePath });
} catch (error) {
throw new Error(`Refusing to trust managed install-state path: ${error.message}`);
}
const initialFingerprint = fingerprintFile(plan.installStatePath);
if (!initialFingerprint.exists) {
return { destinations: new Set(), stateFingerprint: { exists: false, sha256: null } };
}
const readState = dependencies.readInstallState || require('./install-state').readInstallState;
const state = readState(plan.installStatePath);
const validatedFingerprint = fingerprintFile(plan.installStatePath);
if (
initialFingerprint.exists !== validatedFingerprint.exists
|| initialFingerprint.sha256 !== validatedFingerprint.sha256
) {
throw new Error(
`Refusing to trust install-state that changed during validation: ${plan.installStatePath}.`
);
}
assertPriorInstallStateMatchesPlan(state, plan);
const plannedByDestination = new Map(plan.operations.map(operation => [
canonicalPath(operation.destinationPath),
operation,
]));
const destinations = new Set();
for (const operation of state.operations || []) {
if (operation.ownership !== 'managed') {
throw new Error(
`Refusing to trust non-managed ownership from install-state at ${plan.installStatePath}.`
);
}
const destinationPath = operation.destinationPath;
assertWithinTrustedRoot(destinationPath, plan.targetRoot, 'trust install-state ownership');
const canonicalDestination = canonicalPath(destinationPath);View on GitHub (pinned to 8321021c54)