affaan-m/ECC · error · Error
Refusing to write : destination changed after Kimi…
Error message
Refusing to write ${operation.destinationPath}: destination changed after Kimi preflight. What it means
assertOperationUnchanged re-verifies, at write time, that each destination's kind, canonical path, and classification still match what was captured during the Kimi preflight. If the file changed (modified, deleted, replaced by a symlink/directory, or reclassified) between preflight and write, it refuses to write to prevent clobbering user edits or a TOCTOU race.
Solutions
- Re-run the install so preflight and write happen back-to-back on a stable tree
- Ensure no concurrent process modifies the destination during install (stop watchers, other agents, package managers)
- Check for symlinks or mount points that change canonical path and remove/adjust them
- If the change is intentional, review the file first, then re-run preflight
Example fix
// before # editor/formatter rewrote ~/.kimi/config during install -> error // after # close file watchers / pause format-on-save, then re-run the managed install
Defensive patterns
Strategy: retry
Try / catch
try {
await applyMultiHarnessPlan(plan);
} catch (e) {
if (e.message.includes('changed after Kimi preflight')) {
// stop other writers, then rebuild a fresh plan and retry once
const fresh = await createMultiHarnessPlan(request);
await applyMultiHarnessPlan(fresh);
} else throw e;
} Prevention
- Don't edit or format config files while a managed install runs
- Avoid running two installs (or an install and a git checkout) concurrently
- Keep the window between preflight and apply short
- Resolve symlinks in config paths to keep canonical paths stable
When it happens
Trigger: A file at operation.destinationPath was edited, regenerated (e.g. by a concurrent install or a formatter), deleted, or its type changed (file<->symlink<->directory) after preflightManagedPlan ran but before the operation executed.
Common situations: Another process (git checkout, npm install, another agent/harness) touching config files mid-install; running two installs in parallel; user editing the config while the installer runs; slow installs on large projects increasing the race window.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Refusing to read a file that changed during validation
- A normalized install request is required
- An unqualified or incompatible Nasiko executable or receipt…
- Another Nasiko lifecycle operation won lock acquisition
- Another Nasiko lifecycle operation won stale-lock recovery
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/26f7fe4a3dcccc43.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/multi-harness-setup.js:403
const prepareInstallStateWrite = ({ state }) => {
assertStateUnchanged();
expectedStateFingerprint = fingerprintInstallStateValue(state);
};
const assertOperationUnchanged = operation => {
const destination = canonicalPath(operation.destinationPath);
const expected = expectedOperations.get(operation);
const currentClassification = classifyManagedOperation(operation, ownedDestinations);
const expectedClassification = operation.kind === 'merge-json'
&& writtenDestinations.has(destination)
? 'managed-json-update'
: expected && expected.classification;
if (
!expected
|| expected.kind !== operation.kind
|| canonicalPath(expected.destinationPath) !== destination
|| expectedClassification !== currentClassification
) {
throw new Error(
`Refusing to write ${operation.destinationPath}: destination changed after Kimi preflight.`
);
}
return destination;
};
const result = require('./install-executor').applyInstallPlan(preview.plan, {
beforeInstallStateRead() {
assertStateUnchanged();
// Check the original preview before ownership filtering can skip a late
// collision. Guided setup must report the changed plan as a failure.
for (const operation of preview.plan.operations) {
assertOperationUnchanged(operation);
}
},
beforeOperationWrite({ operation }) {
assertStateUnchanged();
const destination = assertOperationUnchanged(operation);View on GitHub (pinned to 8321021c54)