{"record":{"id":"6138b0623f9e0fe0","repo":"paperclipai/paperclip","slug":"refusing-to-remove-unsafe-install-store-path-pat","errorCode":null,"errorMessage":"Refusing to remove unsafe install-store path ${paths.cliRoot}.","messagePattern":"Refusing to remove unsafe install-store path (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"cli/src/install-store.ts","lineNumber":119,"sourceCode":"  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n    try {\n      fs.writeFileSync(paths.markerPath, MANAGED_STORE_MARKER, { mode: 0o600, flag: \"wx\" });\n    } catch (writeError) {\n      if (\n        (writeError as NodeJS.ErrnoException).code !== \"EEXIST\" ||\n        fs.readFileSync(paths.markerPath, \"utf8\") !== MANAGED_STORE_MARKER\n      ) {\n        throw writeError;\n      }\n    }\n  }\n}\n\nexport function assertManagedInstallStore(paths = resolveInstallStorePaths()): InstallManifest {\n  const cliStat = fs.lstatSync(paths.cliRoot);\n  if (!cliStat.isDirectory() || cliStat.isSymbolicLink()) {\n    throw new Error(`Refusing to remove unsafe install-store path ${paths.cliRoot}.`);\n  }\n  assertOwnedByCurrentUser(cliStat, paths.cliRoot);\n  let markerStat: fs.Stats;\n  try {\n    markerStat = fs.lstatSync(paths.markerPath);\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n      throw new Error(`Refusing to remove unverified install store ${paths.cliRoot}.`);\n    }\n    throw error;\n  }\n  if (!markerStat.isFile() || markerStat.isSymbolicLink() || markerStat.nlink > 1) {\n    throw new Error(`Refusing to remove unverified install store ${paths.cliRoot}.`);\n  }\n  assertOwnedByCurrentUser(markerStat, paths.markerPath);\n  if (fs.readFileSync(paths.markerPath, \"utf8\") !== MANAGED_STORE_MARKER) {\n    throw new Error(`Refusing to remove unverified install store ${paths.cliRoot}.`);\n  }","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/install-store.ts#L101-L137","documentation":"Thrown by assertManagedInstallStore() at the very first check: cliRoot is examined with lstatSync and found to be either not a directory or a symlink. This function is the gate before destructive removal operations, so it refuses to act on a path that is not a real directory it controls, preventing accidental deletion of arbitrary targets via symlink redirection.","triggerScenarios":"Called assertManagedInstallStore() (typically before uninstall/prune/remove flows) where paths.cliRoot is a regular file or a symbolic link rather than a real directory.","commonSituations":"1) A symlink was placed at ~/.paperclip/cli. 2) A prior operation left a file at that path. 3) PAPERCLIP_HOME resolves to a location where 'cli' is a symlink or file. 4) Tampering attempt to redirect removal at another directory.","solutions":["Inspect the entry: 'ls -la ~/.paperclip/cli'.","If it is a symlink or stray file you control, remove it so the store can be re-initialized, or point PAPERCLIP_HOME elsewhere.","If the symlink is unexpected, investigate before removing — do not blindly delete.","Once the path is clear, re-run the CLI operation to recreate a genuine store."],"exampleFix":"$ ls -la ~/.paperclip/cli\nlrwxrwxrwx  cli -> /some/other/dir\n$ rm ~/.paperclip/cli\n$ paperclipai install","handlingStrategy":"validation","validationCode":"import fs from \"node:fs\";\nimport { resolveInstallStorePaths } from \"./install-store.js\";\n\nfunction isRealStoreRoot(paths = resolveInstallStorePaths()): boolean {\n  try {\n    const st = fs.lstatSync(paths.cliRoot);\n    return st.isDirectory() && !st.isSymbolicLink();\n  } catch { return false; }\n}","typeGuard":"import fs from \"node:fs\";\n\nfunction isRealDirectory(p: string): boolean {\n  const st = fs.lstatSync(p);\n  return st.isDirectory() && !st.isSymbolicLink();\n}","tryCatchPattern":"try {\n  assertManagedInstallStore(paths);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"unsafe install-store path\")) {\n    console.error(`${paths.cliRoot} is not a real directory; refusing destructive op.`);\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["Never replace ~/.paperclip/cli with a symlink.","Before uninstall, verify the path type manually if anything looks off.","Investigate unexpected symlinks before removing them."],"tags":["install-store","security","symlink-guard","removal-safety","cli"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}