affaan-m/ECC · error
Nasiko cannot install directly into a filesystem root.
Error message
Nasiko cannot install directly into a filesystem root.
What it means
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.
Source
Thrown at scripts/lib/nasiko-release.js:145
});
request.setTimeout(options.timeoutMs || 15000, () => request.destroy(new Error('Nasiko registry request timed out.')));
request.on('error', reject);
});
}
function defaultInstallDirectory(normalized, environment = process.env, homeDirectory = os.homedir()) {
if (normalized.os === 'windows') {
if (!environment.LOCALAPPDATA) throw new Error('LOCALAPPDATA is required on Windows.');
return path.join(environment.LOCALAPPDATA, 'nasiko', 'bin');
}
return path.join(homeDirectory, '.local', 'bin');
}
function validateInstallDirectory(directory) {
if (typeof directory !== 'string' || directory.includes('\0') || !path.isAbsolute(directory)) throw new Error('Nasiko install directory must be an absolute path.');
if (/^(?:\\\\|\\\\\?\\|\\\\\.\\)/.test(directory)) throw new Error('Nasiko install directory must be on a local filesystem.');
const resolved = path.resolve(directory);
if (resolved === path.parse(resolved).root) throw new Error('Nasiko cannot install directly into a filesystem root.');
let ancestor = resolved;
while (!fs.existsSync(ancestor)) {
const parent = path.dirname(ancestor);
if (parent === ancestor) throw new Error('Nasiko install directory has no resolvable filesystem ancestor.');
ancestor = parent;
}
const canonical = fs.realpathSync(ancestor);
return path.join(canonical, path.relative(ancestor, resolved));
}
function assertPrivateInstallDirectory(directory) {
const stats = fs.lstatSync(directory);
if (!stats.isDirectory() || stats.isSymbolicLink()) throw new Error('Nasiko install directory must be a real directory, not a symlink.');
if (process.platform !== 'win32') {
if (typeof process.getuid === 'function' && stats.uid !== process.getuid()) throw new Error('Nasiko install directory must be owned by the current user.');
if ((stats.mode & 0o022) !== 0) throw new Error('Nasiko install directory must not be group- or world-writable.');
}
}View on GitHub (pinned to 06c5e118c4)
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
Example fix
// before: empty subPath collapses the join to the root const dir = path.join(base, subPath); // subPath === '' // after: guarantee a subdirectory const dir = path.join(base, subPath || 'nasiko', 'bin');
Defensive patterns
Strategy: validation
Validate before calling
function assertNotRootDir(dir) {
const resolved = path.resolve(dir);
if (resolved === path.parse(resolved).root) {
throw new Error(`Refusing install directory at filesystem root: ${dir}`);
}
return resolved;
} Try / catch
try {
await installNasiko({ directory: computedDir });
} catch (error) {
if (/cannot install directly into a filesystem root/.test(String(error.message))) {
// A path-join produced '/' or 'C:\'; fix the base/subpath inputs and retry.
}
throw error;
} Prevention
- 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
When it happens
Trigger: 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 '/'.
Common situations: 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'.
Related errors
- Nasiko install directory must be an absolute path.
- Install module ${moduleId} has invalid targets; expected an
- Install module ${moduleId} has unsupported targets: ${unsupp
- Unknown install module: ${unknownModuleIds[0]}
- Unknown install modules: ${unknownModuleIds.join(', ')}
AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18).
Data as JSON: /api/errors/c8537e5f10d4bf02.
Report an issue: GitHub.