abhigyanpatwari/GitNexus · critical · InvalidStoragePathError

Storage path must not be a filesystem root.

Error message

Storage path must not be a filesystem root.

What it means

validateConfiguredStoragePath rejects a storage path whose basename is empty after validation, i.e. a filesystem root like '/' or 'C:\'. GitNexus storage must live in a named directory slot; pointing storage at a root would make deletion/ownership logic operate on the entire volume, so it fails closed.

Solutions

  1. Point storage at a dedicated subdirectory, e.g. /var/lib/gitnexus-storage instead of /.
  2. Strip trailing slashes and ensure a real directory name is present in the configured value.
  3. Validate config at startup and fail with a friendly message before invoking storage APIs.

Example fix

// before
export GITNEXUS_STORAGE_PATH=/
// after
export GITNEXUS_STORAGE_PATH=/var/lib/gitnexus-storage
Defensive patterns

Strategy: validation

Validate before calling

const resolvedValue = path.resolve(value);
if (path.basename(resolvedValue).length === 0) {
  throw new Error('storage path must be a named directory, not a filesystem root');
}

Type guard

const isNamedDirPath = (v: string): boolean => path.basename(path.resolve(v)).length > 0;

Try / catch

try {
  const p = configuredStoragePath(value);
} catch (e) {
  if (e instanceof InvalidStoragePathError && e.message.includes('root')) {
    throw new Error('GITNEXUS_STORAGE_PATH points at a filesystem root; use a dedicated subdirectory');
  }
  throw e;
}

Prevention

When it happens

Trigger: Setting GITNEXUS_STORAGE_PATH or GITNEXUS_STORAGE_ROOT to '/' (or a drive root), or a value like '/data/repos/' that normalizes so basename is empty; calling configuredStoragePath/registeredStoragePath/resolved/resolvedStoragePath with such a value.

Common situations: Env var placeholder left as '/'; config template with trailing-slash root value; someone attempting to use a mounted volume root as the storage root.

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/f28db4c612a1a11a. Report an issue: GitHub.

Appendix: source

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

  const canonical = canonicalRepoPath(repoPath);
  const identity = process.platform === 'win32' ? canonical.toLowerCase() : canonical;
  const basename = sanitizeSlotBasename(path.basename(canonical));
  const digest = createHash('sha256')
    .update(identity)
    .digest('hex')
    .slice(0, STORAGE_SLOT_HASH_LENGTH);
  return `${basename}-${digest}`;
};

export const defaultStoragePath = (repoPath: string): string =>
  path.join(resolveRepoPath(repoPath), GITNEXUS_DIR);

export const validateConfiguredStoragePath = (value: string): string => {
  const resolved = validateAbsolutePath(value, 'Storage path');
  const parent = path.dirname(resolved);
  const base = path.basename(resolved);
  if (base.length === 0) {
    throw new InvalidStoragePathError('Storage path must not be a filesystem root.');
  }
  // Rebuild through parent + basename and apply the path.relative idiom
  // CodeQL's js/path-injection sanitizer recognizes. The reconstructed path
  // is what callers pass to filesystem APIs.
  const inspected = path.resolve(parent, base);
  const rel = path.relative(parent, inspected);
  if (rel.startsWith('..') || path.isAbsolute(rel)) {
    throw new InvalidStoragePathError('Storage path escaped its parent directory.');
  }
  return inspected;
};

/** Resolve one repository's isolated slot under an external storage root. */
export const storagePathFromRoot = (rootPath: string, repoPath: string): string => {
  const root = validateAbsolutePath(rootPath, STORAGE_ROOT_ENV);
  const storagePath = path.resolve(root, storageSlotName(repoPath));
  const rel = path.relative(root, storagePath);
  if (

View on GitHub (pinned to ac9a4e9abd)