affaan-m/ECC · error · Error

Refusing to hash changed install destination: ${operation.de

Error message

Refusing to hash changed install destination: ${operation.destinationPath}

What it means

TOCTOU guard in readInstalledFileNoFollow: fstat/lstat identity comparison detects the destination changed, is a symlink, or is no longer a regular file between open and stat, so hashing (and trusting) its content is refused.

Source

Thrown at scripts/lib/install/apply.js:107

  try {
    descriptor = fs.openSync(operation.destinationPath, flags);
  } catch (error) {
    if (error.code === 'ENOENT') {
      return null;
    }
    throw error;
  }

  try {
    const openedStat = fs.fstatSync(descriptor, { bigint: true });
    const finalPathStat = fs.lstatSync(operation.destinationPath, { bigint: true });
    if (finalPathStat.isSymbolicLink() || !finalPathStat.isFile()) {
      return null;
    }
    const identityMatches = openedStat.ino === finalPathStat.ino
      && (!openedStat.dev || !finalPathStat.dev || openedStat.dev === finalPathStat.dev);
    if (!openedStat.isFile() || !identityMatches) {
      throw new Error(
        `Refusing to hash changed install destination: ${operation.destinationPath}`
      );
    }
    // Revalidate the full path after opening. The descriptor pins the file so
    // the digest and metadata refer to the same object.
    assertSafeInstallOperation(plan, operation);
    assertSafeClaudeSkillOperation(plan, operation);
    return fs.readFileSync(descriptor);
  } finally {
    fs.closeSync(descriptor);
  }
}

function stateWithContentDigests(state, plan) {
  return {
    ...state,
    operations: (state.operations || []).map(operation => {
      if (!operation.destinationPath) {

View on GitHub (pinned to 06c5e118c4)

Solutions

  1. The destination file changed after hashing; re-run the install so state is recomputed.
  2. If you edited the managed file manually, restore it or run a repair so ECC can manage it safely.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at scripts/lib/install/apply.js:107 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18). Data as JSON: /api/errors/3a0ddeaefb0587f7. Report an issue: GitHub.