abhigyanpatwari/GitNexus · error · InvalidStoragePathError

must be an absolute path.

Error message

${label} must be an absolute path.

What it means

validateAbsolutePath enforces that the labeled value is already absolute. A relative path like 'storage/repos/x' fails with '<label> must be an absolute path.' even though it is non-empty. This guarantees downstream filesystem operations use unambiguous locations independent of the process cwd.

Solutions

  1. Resolve to absolute before the call: path.resolve(value), or better require an absolute value in config and document it.
  2. Prefix with the intended base: path.join('/var/lib/gitnexus', relativeValue) when the relative path is intentional.
  3. Fix env/config to a full path (leading '/' on POSIX, drive letter 'C:\\' on Windows).

Example fix

// before
resolved(process.env.GITNEXUS_STORAGE_PATH); // './data/gitnexus'
// after
const p = path.resolve(process.env.GITNEXUS_STORAGE_PATH ?? path.join(os.homedir(), '.gitnexus'));
resolved(p);
Defensive patterns

Strategy: validation

Validate before calling

if (typeof value !== 'string' || !path.isAbsolute(value)) {
  throw new Error(`${label} must be an absolute path, e.g. ${path.join(os.homedir(), '.gitnexus')}`);
}

Type guard

const isAbsolutePath = (v: unknown): v is string => typeof v === 'string' && path.isAbsolute(v);

Try / catch

try {
  const root = root(value);
} catch (e) {
  if (e instanceof InvalidStoragePathError && e.message.includes('absolute')) {
    throw new Error(`${label} was relative; provide a full path (leading / or drive letter)`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing a relative path (path.isAbsolute === false) to resolved(), root(), or readOwnershipMetadata(), e.g. GITNEXUS_STORAGE_PATH='gitnexus-data' or './data'.

Common situations: Env/config written for a different working directory; copying examples with placeholder relative paths; platform mixups (Windows drive-less paths like 'C:foo' or POSIX-style paths interpreted relatively).

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@ac9a4e9abd (2026-09-15). Data as JSON: /api/errors/8df0dbbbc716eeb8. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/storage/storage-resolver.ts:191

const resolveRepoPath = (value: string): string => {
  if (typeof value !== 'string' || value.length === 0) {
    throw new InvalidStoragePathError('Repository path must be non-empty.');
  }
  if (value.includes('\0')) {
    throw new InvalidStoragePathError('Repository path must not contain a NUL character.');
  }
  return path.resolve(value);
};

const validateAbsolutePath = (value: string, label: string): string => {
  if (typeof value !== 'string' || value.length === 0) {
    throw new InvalidStoragePathError(`${label} must be an absolute, non-empty path.`);
  }
  if (value.includes('\0')) {
    throw new InvalidStoragePathError(`${label} must not contain a NUL character.`);
  }
  if (!path.isAbsolute(value)) {
    throw new InvalidStoragePathError(`${label} must be an absolute path.`);
  }
  return path.resolve(value);
};

// Mirror registry lookup semantics without importing repo-manager and creating a cycle.
const canonicalRegistryPath = (value: string): string => {
  const resolved = path.resolve(value);
  try {
    return stripWindowsLongPathPrefix(fs.realpathSync.native(resolved));
  } catch {
    return stripWindowsLongPathPrefix(resolved);
  }
};

const canonicalRepoPath = (repoPath: string): string =>
  canonicalRegistryPath(resolveRepoPath(repoPath));

const comparablePath = (value: string): string => {

View on GitHub (pinned to ac9a4e9abd)