abhigyanpatwari/GitNexus · error · InvalidStoragePathError

must not contain a NUL character.

Error message

${label} must not contain a NUL character.

What it means

validateAbsolutePath rejects labeled absolute-path values that contain a NUL character, mirroring the repo-path check. NUL bytes terminate C-string paths and are a classic injection marker, so the resolver fails closed before any filesystem API runs.

Solutions

  1. Sanitize at the boundary: reject any value containing '\0' with a clear caller-side error.
  2. If from a Buffer, decode correctly (utf8) and strip trailing NULs before use.
  3. Regenerate corrupted metadata/config files instead of patching the tainted string.

Example fix

// before
root(userSuppliedPath);
// after
if (userSuppliedPath.includes('\0')) throw new Error('storage root must not contain NUL');
root(userSuppliedPath);
Defensive patterns

Strategy: validation

Validate before calling

if (typeof value !== 'string' || value.includes('\0')) {
  throw new Error(`${label} must not contain NUL characters`);
}

Type guard

const isNulFreeString = (v: unknown): v is string => typeof v === 'string' && !v.includes('\0');

Try / catch

try {
  return readOwnershipMetadata(value);
} catch (e) {
  if (e instanceof InvalidStoragePathError && e.message.includes('NUL')) {
    throw new Error(`${label} contained a NUL byte — regenerate the metadata file`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling resolved()/root()/readOwnershipMetadata() with a storage path containing '\0' — from unsanitized user input, wrong-encoding buffer decodes, or corrupted config/ownership metadata files.

Common situations: Ownership metadata files (.gitnexus) hand-edited or truncated at a NUL; HTTP-supplied storage paths passed straight through; byte-padded Windows path conversions.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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

Appendix: source

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

  return code ? `${code}: ${(error as Error)?.message ?? String(error)}` : String(error);
};

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 =>

View on GitHub (pinned to ac9a4e9abd)