abhigyanpatwari/GitNexus · error · BadRequestError

Path must not be empty

Error message

Path must not be empty

What it means

assertSafePath resolves a user-supplied relative path against an allowed root and verifies containment. It rejects the empty string up front with 400 because an empty path is almost always a missing or defaulted parameter rather than a meaningful target — and passing '' on to path.resolve/fs calls yields surprising behavior instead of a clean error.

Solutions

  1. Omit the parameter when there is no path, or send '.' if the root itself is the intended target
  2. Gate the request client-side on the field being non-empty after trim
  3. Initialize optional path state to null/undefined, never ''

Example fix

// before
const url = `/api/file?path=${encodeURIComponent(filePath ?? '')}`;

// after
const params = new URLSearchParams();
if (filePath) params.set('path', filePath);
const url = `/api/file${params.size ? `?${params}` : ''}`;
Defensive patterns

Strategy: validation

Validate before calling

const p = (pathParam ?? '').trim();
if (!p) {
  // omit the parameter instead of sending an empty string
  delete params.path;
}

Prevention

When it happens

Trigger: Calling an endpoint that takes a relative path parameter with path= (empty value), or a client that defaults the field to '' and sends it anyway instead of omitting it.

Common situations: Optional-path UI controls initialized to '' instead of null/undefined; template strings like `${base}${rest}` with rest empty; forms submitting untouched inputs.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/8bfd149cab25c2c1. Report an issue: GitHub.

Appendix: source

Thrown at gitnexus/src/server/validation.ts:79

    }
    throw new BadRequestError(`Parameter "${fieldName}" must be a string`);
  }
  return value;
}

/**
 * Resolve a user-supplied relative path against an allowed root and verify it
 * stays inside that root. Mirrors the existing guard at api.ts:1067-1077.
 *
 * Returns the absolute resolved path. Rejects empty paths, null bytes, and
 * paths that resolve outside the root (e.g., `../../../etc/passwd`).
 *
 * @throws BadRequestError when the path is empty or contains a null byte
 * @throws ForbiddenError when the resolved path escapes the root
 */
export function assertSafePath(rawPath: string, root: string): string {
  if (rawPath.length === 0) {
    throw new BadRequestError('Path must not be empty');
  }
  if (rawPath.includes('\0')) {
    throw new BadRequestError('Path must not contain null bytes');
  }
  const resolvedRoot = path.resolve(root);
  const fullPath = path.resolve(resolvedRoot, rawPath);
  const safePrefix = resolvedRoot.endsWith(path.sep) ? resolvedRoot : resolvedRoot + path.sep;
  if (fullPath !== resolvedRoot && !fullPath.startsWith(safePrefix)) {
    throw new ForbiddenError('Path traversal denied');
  }
  return fullPath;
}

/**
 * Escape regex metacharacters in a user-supplied string so it can be safely
 * embedded as a literal in `new RegExp(...)`. Used by /api/grep's literal mode
 * and any future endpoint that constructs a regex from caller input.
 */

View on GitHub (pinned to aac7515d2a)