{"record":{"id":"c8537e5f10d4bf02","repo":"affaan-m/ECC","slug":"nasiko-cannot-install-directly-into-a-filesystem-r","errorCode":null,"errorMessage":"Nasiko cannot install directly into a filesystem root.","messagePattern":"Nasiko cannot install directly into a filesystem root\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/nasiko-release.js","lineNumber":145,"sourceCode":"    });\n    request.setTimeout(options.timeoutMs || 15000, () => request.destroy(new Error('Nasiko registry request timed out.')));\n    request.on('error', reject);\n  });\n}\n\nfunction defaultInstallDirectory(normalized, environment = process.env, homeDirectory = os.homedir()) {\n  if (normalized.os === 'windows') {\n    if (!environment.LOCALAPPDATA) throw new Error('LOCALAPPDATA is required on Windows.');\n    return path.join(environment.LOCALAPPDATA, 'nasiko', 'bin');\n  }\n  return path.join(homeDirectory, '.local', 'bin');\n}\n\nfunction validateInstallDirectory(directory) {\n  if (typeof directory !== 'string' || directory.includes('\\0') || !path.isAbsolute(directory)) throw new Error('Nasiko install directory must be an absolute path.');\n  if (/^(?:\\\\\\\\|\\\\\\\\\\?\\\\|\\\\\\\\\\.\\\\)/.test(directory)) throw new Error('Nasiko install directory must be on a local filesystem.');\n  const resolved = path.resolve(directory);\n  if (resolved === path.parse(resolved).root) throw new Error('Nasiko cannot install directly into a filesystem root.');\n  let ancestor = resolved;\n  while (!fs.existsSync(ancestor)) {\n    const parent = path.dirname(ancestor);\n    if (parent === ancestor) throw new Error('Nasiko install directory has no resolvable filesystem ancestor.');\n    ancestor = parent;\n  }\n  const canonical = fs.realpathSync(ancestor);\n  return path.join(canonical, path.relative(ancestor, resolved));\n}\n\nfunction assertPrivateInstallDirectory(directory) {\n  const stats = fs.lstatSync(directory);\n  if (!stats.isDirectory() || stats.isSymbolicLink()) throw new Error('Nasiko install directory must be a real directory, not a symlink.');\n  if (process.platform !== 'win32') {\n    if (typeof process.getuid === 'function' && stats.uid !== process.getuid()) throw new Error('Nasiko install directory must be owned by the current user.');\n    if ((stats.mode & 0o022) !== 0) throw new Error('Nasiko install directory must not be group- or world-writable.');\n  }\n}","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/affaan-m/ECC/blob/06c5e118c4d3e6c3b7f9445f973a2194c82de193/scripts/lib/nasiko-release.js#L127-L163","documentation":"The third gate in validateInstallDirectory resolves the directory and refuses when the result equals the filesystem root (path.parse(resolved).root, e.g. '/' or 'C:\\'). Installing 'directly into root' would scatter binary and metadata files at the top of the drive, which is destructive and almost always a path-construction bug, so it is rejected before the ancestor-resolution logic runs.","triggerScenarios":"path.resolve(directory) === path.parse(path.resolve(directory)).root. Happens when a base path is empty and the join collapses to '/', when a Windows config yields 'C:\\', or when directory is exactly '/'.","commonSituations":"path.join(base, sub) where base is '' and sub is '' or '/'; misparsed CLI flags like --dir=/; Windows configs where the drive letter is kept but the subpath variable is empty; container setups that pass '/' intending 'install anywhere'.","solutions":["Pass a real subdirectory, e.g. path.join(base || os.homedir(), '.local', 'bin')","Guard config-derived paths: if the resolved value equals the root, fall back to the default install directory","Log the computed directory before invoking the installer so misconfiguration is visible"],"exampleFix":"// before: empty subPath collapses the join to the root\nconst dir = path.join(base, subPath); // subPath === ''\n// after: guarantee a subdirectory\nconst dir = path.join(base, subPath || 'nasiko', 'bin');","handlingStrategy":"validation","validationCode":"function assertNotRootDir(dir) {\n  const resolved = path.resolve(dir);\n  if (resolved === path.parse(resolved).root) {\n    throw new Error(`Refusing install directory at filesystem root: ${dir}`);\n  }\n  return resolved;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await installNasiko({ directory: computedDir });\n} catch (error) {\n  if (/cannot install directly into a filesystem root/.test(String(error.message))) {\n    // A path-join produced '/' or 'C:\\'; fix the base/subpath inputs and retry.\n  }\n  throw error;\n}","preventionTips":["Never join config-derived path segments without validating the result is a subdirectory","Fall back to the default install directory when base/subPath inputs are empty","Log the final computed directory before invoking the installer to catch collapse-to-root bugs"],"tags":["path","filesystem-root","validation","install"],"backgroundTag":"filesystem-root-write-rejected","analyzedSha":"06c5e118c4d3e6c3b7f9445f973a2194c82de193","analyzedAt":"2026-08-18T11:27:13.915Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}