{"record":{"id":"dd3536e4cf400242","repo":"affaan-m/ECC","slug":"refusing-to-trust-install-state-that-changed-durin","errorCode":null,"errorMessage":"Refusing to trust install-state that changed during validation: ${plan.installStatePath}.","messagePattern":"Refusing to trust install-state that changed during validation: (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/multi-harness-setup.js","lineNumber":145,"sourceCode":"    return { destinations: new Set(), stateFingerprint: { exists: false, sha256: null } };\n  }\n  try {\n    assertSafeInstallOperation(plan, { destinationPath: plan.installStatePath });\n  } catch (error) {\n    throw new Error(`Refusing to trust managed install-state path: ${error.message}`);\n  }\n  if (!fs.existsSync(plan.installStatePath)) {\n    return { destinations: new Set(), stateFingerprint: { exists: false, sha256: null } };\n  }\n  const readState = dependencies.readInstallState || require('./install-state').readInstallState;\n  const initialFingerprint = fingerprintFile(plan.installStatePath);\n  const state = readState(plan.installStatePath);\n  const validatedFingerprint = fingerprintFile(plan.installStatePath);\n  if (\n    initialFingerprint.exists !== validatedFingerprint.exists\n    || initialFingerprint.sha256 !== validatedFingerprint.sha256\n  ) {\n    throw new Error(\n      `Refusing to trust install-state that changed during validation: ${plan.installStatePath}.`\n    );\n  }\n  assertPriorInstallStateMatchesPlan(state, plan);\n  const plannedByDestination = new Map(plan.operations.map(operation => [\n    canonicalPath(operation.destinationPath),\n    operation,\n  ]));\n  const destinations = new Set();\n  for (const operation of state.operations || []) {\n    if (operation.ownership !== 'managed') {\n      throw new Error(\n        `Refusing to trust non-managed ownership from install-state at ${plan.installStatePath}.`\n      );\n    }\n    const destinationPath = operation.destinationPath;\n    assertWithinTrustedRoot(destinationPath, plan.targetRoot, 'trust install-state ownership');\n    const canonicalDestination = canonicalPath(destinationPath);","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/multi-harness-setup.js#L127-L163","documentation":"Thrown by readOwnedDestinations as a TOCTOU guard around the read itself. The installer fingerprints the install-state (initialFingerprint), calls readInstallState, then fingerprints again (validatedFingerprint); if the two fingerprints differ, the file was modified while being read and the parsed state cannot be trusted. JSON parsers tolerate partial writes, so this check is what catches a concurrent mutation that would otherwise produce silently wrong ownership data.","triggerScenarios":"Fires when initialFingerprint and validatedFingerprint (exists or sha256) differ inside readOwnedDestinations. Caused by another process writing the install-state between the two fingerprintFile calls: a concurrent ECC run, a file watcher/linter rewriting JSON, an editor autosave, a sync tool, or a partial write from a crashed previous run being flushed.","commonSituations":"Two terminal sessions running guided install in parallel; an IDE formatting/saving .claude/install-state.json during install; cloud-sync (Dropbox/OneDrive) rewriting the file; a previous ECC process crashed mid-write and the OS is finalizing; antivirus locking/rewriting the file on Windows.","solutions":["Close other processes that may write the install-state (other ECC runs, editors with the file open, sync clients) and retry.","Re-run the guided preview to capture a stable fingerprint and apply immediately.","Move the project out of a synced directory during install, or pause sync.","If it recurs, check for a crashed prior ECC process still holding/rewriting the file."],"exampleFix":"// Hard to show a code fix; this is an environmental race. Mitigate by serializing installs:\n\n// before: two concurrent installs race the same state file\n// terminal A: applyMultiHarnessPlan(planA)  // writes install-state\n// terminal B: applyMultiHarnessPlan(planB)  // throws [285]\n\n// after: run one install at a time per project\nawait applyMultiHarnessPlan(planA); // completes, releases state\n// only then:\nawait applyMultiHarnessPlan(planB);","handlingStrategy":"retry","validationCode":"const fs = require('fs'); const crypto = require('crypto');\nfunction fingerprint(p) {\n  if (!fs.existsSync(p)) return { exists: false, sha256: null };\n  return { exists: true, sha256: crypto.createHash('sha256').update(fs.readFileSync(p)).digest('hex') };\n}\nfunction assertStateQuiescent(statePath) {\n  const a = fingerprint(statePath);\n  const b = fingerprint(statePath);\n  if (a.exists !== b.exists || a.sha256 !== b.sha256) {\n    throw new Error(`Install-state is being modified concurrently; stop other writers and retry.`);\n  }\n}\nassertStateQuiescent(plan.installStatePath);","typeGuard":"null","tryCatchPattern":"async function applyWithQuiescence(plan, attempts = 3) {\n  for (let i = 0; i < attempts; i++) {\n    try {\n      return await applyMultiHarnessPlan(plan);\n    } catch (err) {\n      if (/changed during validation/.test(err.message) && i < attempts - 1) continue;\n      throw err;\n    }\n  }\n}","preventionTips":["Serialize installs: never run two ECC installs against the same project at once.","Close editors and pause sync clients that may rewrite .claude/install-state.json.","On Windows, exclude the project from antivirus scanning during install if it rewrites files.","If a prior ECC run crashed, verify no orphan process is still writing before retrying."],"tags":["install-state","toctou","concurrency","fingerprint","kimi"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}