{"record":{"id":"e21f694d4ad54061","repo":"affaan-m/ECC","slug":"refusing-install-a-user-owned-file-appeared-at-operation","errorCode":null,"errorMessage":"Refusing install: a user-owned file appeared at ${operation.destinationPath} after planning. Rerun the installer to preserve it.","messagePattern":"Refusing install: a user-owned file appeared at (.+?) after planning\\. Rerun the installer to preserve it\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install/ownership-guard.js","lineNumber":134,"sourceCode":"      ...skippedOperations,\n    ],\n    warnings: [...((migration && migration.warnings) || []), ...warnings],\n    bridgeState,\n    finalState,\n    // Only keep bridge persistence when operations actually remain; a fully\n    // skipped plan installs nothing and must not claim anything.\n    requiresBridgeState: Boolean(migration.requiresBridgeState)\n      && appliedOperations.length > 0,\n  };\n}\n\nfunction assertNoNewUserOwnedFile(migration, operation) {\n  if (operation.kind !== 'copy-file'\n    || migration.managedDestinations.has(comparablePath(operation.destinationPath))\n    || !pathExists(operation.destinationPath)) {\n    return;\n  }\n  throw new Error(`Refusing install: a user-owned file appeared at ${operation.destinationPath} after planning. Rerun the installer to preserve it.`);\n}\n\nfunction preserveUnwrittenFiles(state, migration, writtenDestinations) {\n  const writtenPaths = new Set([...writtenDestinations].map(comparablePath));\n  return {\n    ...state,\n    operations: state.operations.filter(operation => (\n      operation.kind !== 'copy-file'\n      || migration.managedDestinations.has(comparablePath(operation.destinationPath))\n      || writtenPaths.has(comparablePath(operation.destinationPath))\n      || !pathExists(operation.destinationPath)\n    )).map(operation => {\n      const destination = comparablePath(operation.destinationPath);\n      return operation.kind === 'copy-file' && !writtenPaths.has(destination)\n        ? migration.previousManagedOperations.get(destination) || operation\n        : operation;\n    }),\n  };","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install/ownership-guard.js#L116-L152","documentation":"assertNoNewUserOwnedFile is a last-line defense run while applying the install plan (under lock). For copy-file operations whose destination was NOT already a managed destination, it checks whether a file now exists at the destination that did not exist at planning time. If so, it assumes the user placed a file there and refuses the copy so the user's file is preserved.","triggerScenarios":"Calling applyInstallPlanLocked → assertNoNewUserOwnedFile when: (1) pathExists(operation.destinationPath) is true at apply time but the destination was absent (and unmanaged) during planning; (2) something created a file at that path between plan and apply — another process, a git checkout, or a hook.","commonSituations":"User creates a config file while the installer is running; a package manager or scaffolding tool writes the destination concurrently; rerunning an install after a partial failure where a previously-unmanaged file appeared; copying a repo that now contains the destination file.","solutions":["Rerun the installer as the message says — the new planning pass will detect the existing file and treat it as user-owned (skip/preserve).","If the file is not yours and should be overwritten, delete it or mark it as managed, then reinstall.","Ensure no other process writes to the install directory while the installer runs.","Verify the destination isn't being recreated by a git operation or postinstall script between plan and apply."],"exampleFix":"// before: file appeared after planning → install aborts\napplyInstallPlan(plan);\n\n// after: re-plan so the existing user file is detected and preserved\nconst freshPlan = buildInstallPlan(options); // planning now sees the user file\napplyInstallPlan(freshPlan);","handlingStrategy":"validation","validationCode":"if (fs.existsSync(dest) && !plan.managedDestinations.has(norm(dest))) {\n  console.warn(`user file exists at ${dest}; rerun installer to re-plan before applying`);\n}","typeGuard":"function destinationIsSafeToCopy(op, managedDestinations) {\n  return op.kind !== 'copy-file'\n    || managedDestinations.has(norm(op.destinationPath))\n    || !fs.existsSync(op.destinationPath);\n}","tryCatchPattern":"try {\n  applyInstallPlanLocked(plan);\n} catch (e) {\n  if (e.message.includes('user-owned file appeared')) {\n    const fresh = buildInstallPlan(plan.options); // re-plan sees the new user file\n    return applyInstallPlanLocked(fresh);\n  }\n  throw e;\n}","preventionTips":["Don't write into the install directory while the installer runs.","Rerun planning immediately before applying; don't reuse old plans.","Commit or stash local changes before scripted installs.","Disable postinstall/git hooks that create files in managed destinations mid-install."],"tags":["installer","race-condition","file-conflict"],"backgroundTag":"file-already-exists","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}