affaan-m/ECC · error

Nasiko install directory must be an absolute path.

Error message

Nasiko install directory must be an absolute path.

What it means

validateInstallDirectory in scripts/lib/nasiko-release.js is the first gate on caller-supplied install paths: the value must be a string, contain no NUL bytes, and be absolute (path.isAbsolute). Relative or malformed input is rejected before any filesystem work happens. The subsequent checks (local filesystem, not a root, resolvable ancestor) run only after this passes.

Source

Thrown at scripts/lib/nasiko-release.js:142

      });
      response.on('end', () => resolve(Buffer.concat(chunks)));
      response.on('error', reject);
    });
    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.');

View on GitHub (pinned to 06c5e118c4)

Solutions

  1. Expand and resolve before calling: `path.resolve(rawDir.replace(/^~(?=[/\\]|$)/, os.homedir()))`
  2. Use path.join(os.homedir(), '.local', 'bin') or another constructed absolute path
  3. Validate config inputs with a schema that requires an absolute path string

Example fix

// before: tilde is relative to Node, not the shell
installNasiko({ directory: '~/.local/bin' }); // throws
// after
installNasiko({ directory: path.join(os.homedir(), '.local', 'bin') });
Defensive patterns

Strategy: validation

Validate before calling

const fs = require('fs');
const os = require('os');
const path = require('path');

function toInstallDir(raw) {
  const expanded = raw.replace(/^~(?=[/\\]|$)/, os.homedir());
  const dir = path.resolve(expanded);
  if (!path.isAbsolute(dir)) throw new Error(`Install directory must be absolute: ${raw}`);
  return dir;
}

Type guard

const isAbsoluteLocalPath = (p: unknown): p is string =>
  typeof p === 'string' && !p.includes('\0') && path.isAbsolute(p);

Try / catch

try {
  await installNasiko({ directory: configuredDir });
} catch (error) {
  if (/must be an absolute path/.test(String(error.message))) {
    // Expand '~' and resolve relatives before retrying; schema-validate config.
  }
  throw error;
}

Prevention

When it happens

Trigger: Passing directory: '~/.local/bin' (tilde is not expanded by Node, so it is relative), './bin', 'nasiko/bin', undefined, a non-string value, or a string containing a NUL character.

Common situations: Shell-style paths copied into JS configs where '~' is never expanded; config files with relative paths that worked with other tools (which expand or chdir first); templating bugs injecting empty strings; values sourced from JSON that arrive as null.

Related errors


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