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
- Point storage at a dedicated subdirectory, e.g. /var/lib/gitnexus-storage instead of /.
- Strip trailing slashes and ensure a real directory name is present in the configured value.
- 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
- Never configure '/' or a drive root as storage; always use a dedicated directory.
- Trim trailing separators and verify basename is non-empty when building config.
- Fail fast at startup with a clear message pointing at the offending env var.
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
- Analysis not finalized: missing
- Could not remove the shadowed branch sub-index; keeping its…
- DiskBackedScopeTree.byId is unsupported…
- err.message
- entry does not identify a repo — name and storagePath must…
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)