affaan-m/ECC · error · Error
Refusing to overwrite an unowned or changed install-state at
Error message
Refusing to overwrite an unowned or changed install-state at ${plan.installStatePath}. Re-run the guided preview and review the existing state before retrying. What it means
Thrown by assertInstallStateUnchanged when the managed install-state file (plan.installStatePath) no longer matches the SHA-256 fingerprint captured during preflight. The installer fingerprints the state file before planning and re-checks it immediately before each write (beforeOperationWrite and beforeInstallStateWrite), refusing to proceed if the bytes changed in between. This is a deliberate TOCTOU (time-of-check/time-of-use) guard: the install-state is the sole proof of which destinations ECC owns, so silently overwriting a mutated state could clobber files the user edited.
Source
Thrown at scripts/lib/multi-harness-setup.js:91
}
function operationIdentityMatches(stateOperation, plannedOperation) {
return [
'kind',
'moduleId',
'sourceRelativePath',
'strategy',
'scaffoldOnly',
].every(field => stateOperation[field] === plannedOperation[field]);
}
function assertInstallStateUnchanged(plan, expectedFingerprint) {
const currentFingerprint = fingerprintFile(plan.installStatePath);
if (
currentFingerprint.exists !== expectedFingerprint.exists
|| currentFingerprint.sha256 !== expectedFingerprint.sha256
) {
throw new Error(
`Refusing to overwrite an unowned or changed install-state at ${plan.installStatePath}. `
+ 'Re-run the guided preview and review the existing state before retrying.'
);
}
}
function assertPriorInstallStateMatchesPlan(state, plan) {
const target = state.target || {};
const adapter = plan.adapter || {};
if (
target.id !== adapter.id
|| target.target !== adapter.target
|| target.kind !== adapter.kind
) {
throw new Error(
`Refusing to trust managed install-state at ${plan.installStatePath}: `
+ 'target identity does not match the current Kimi install plan.'
);View on GitHub (pinned to 01e15490f0)
Solutions
- Re-run the guided preview (createMultiHarnessPlan with dryRun) to regenerate the fingerprint against the current state, then apply immediately without touching the file.
- If the existing install-state is stale or corrupt, delete plan.installStatePath and re-run the preview so ECC records fresh ownership.
- Ensure no other ECC/install process, editor, or sync agent is touching the install-state between preview and apply.
- Inspect the file at plan.installStatePath to confirm whether the change was intentional before discarding it.
Example fix
// before: preview and apply separated by manual state edits
const preview = await createMultiHarnessPlan(req, {}, opts);
// user edits .claude/install-state.json here -> fingerprint drifts
await applyMultiHarnessPlan(preview, {}, opts); // throws [280]
// after: preview and apply back-to-back, no intervening mutation
const preview = await createMultiHarnessPlan(req, {}, opts);
await applyMultiHarnessPlan(preview, {}, opts); Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
function assertInstallStateStable(installStatePath, expectedFingerprint) {
if (!installStatePath || !expectedFingerprint) return; // fresh install, nothing to check
const exists = fs.existsSync(installStatePath);
if (exists !== expectedFingerprint.exists) {
throw new Error(`Install-state existence changed; re-run preview before apply.`);
}
if (exists) {
const crypto = require('crypto');
const sha = crypto.createHash('sha256').update(fs.readFileSync(installStatePath)).digest('hex');
if (sha !== expectedFingerprint.sha256) {
throw new Error(`Install-state digest drift; re-run preview before apply.`);
}
}
}
// call right before applyMultiHarnessPlan:
assertInstallStateStable(preview.harnesses.find(h=>h.id==='kimi').preview.plan.installStatePath,
preview.harnesses.find(h=>h.id==='kimi').preview.ownershipSnapshot.stateFingerprint); Type guard
null
Try / catch
try {
await applyMultiHarnessPlan(preview, {}, options);
} catch (err) {
if (/Refusing to overwrite an unowned or changed install-state/.test(err.message)) {
// state drifted between preview and apply: re-preflight and retry once
const repreview = await createMultiHarnessPlan(preview.request, {}, options);
await applyMultiHarnessPlan(repreview, {}, options);
} else throw err;
} Prevention
- Run createMultiHarnessPlan and applyMultiHarnessPlan back-to-back with no filesystem mutation in between.
- Treat the install-state file as append-only by ECC; never edit it manually.
- Avoid running two installs against the same project concurrently.
- Pause cloud-sync and close editors with the install-state open during install.
When it happens
Trigger: Called inside applyPreflightedManagedPlan via assertStateUnchanged() at beforeOperationWrite and beforeInstallStateWrite. Fires when fingerprintFile(plan.installStatePath) returns exists/sha256 differing from preview.ownershipSnapshot.stateFingerprint. This happens if, between createMultiHarnessPlan->preflightManaged and applyMultiHarnessPlan->applyManaged, the install-state JSON was edited, deleted, rewritten by another ECC process, touched by a sync tool (Dropbox/iCloud/git checkout), or the path started returning a different realpath.
Common situations: Running two guided installs concurrently in the same project; a previous partial install left a half-written state; the user hand-edited .claude/install-state.json; a git branch switch or filesystem sync changed the file between preview and apply; symlinks resolving differently between runs.
Related errors
- Refusing to trust install-state that changed during validati
- Refusing to trust managed install-state at ${plan.installSta
- Refusing to trust managed install-state at ${plan.installSta
- Refusing to trust managed install-state at ${plan.installSta
- Refusing to trust managed install-state path: ${error.messag
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/a407012f3abd721f.
Report an issue: GitHub.