abhigyanpatwari/GitNexus · error

Invalid group name " ". Names must start with a letter or…

Error message

Invalid group name "${name}". Names must start with a letter or digit and contain only [a-zA-Z0-9_-].

What it means

Thrown by validateGroupName() in gitnexus/src/core/group/storage.ts before any group directory path is built. A group name must match /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/ — start with a letter or digit, then only letters, digits, underscore, or hyphen. The guard exists because the name is joined directly into a filesystem path (getGroupDir), so anything else (spaces, slashes, dots, leading dash) is rejected to prevent malformed or traversing paths.

Solutions

  1. Rename the group to match [a-zA-Z0-9][a-zA-Z0-9_-]* (e.g. "my repo" -> "my-repo", "org/team" -> "org-team")
  2. Trim whitespace and strip/replace disallowed characters (slash, dot, @, spaces) in the script that generates the name
  3. If the name came from a repo path, use only the final path segment and sanitize it before passing it to the group command

Example fix

# before
$ gitnexus group create "@org/my team"
# after
$ gitnexus group create "org-my-team"
Defensive patterns

Strategy: validation

Validate before calling

const GROUP_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
const name = rawInput.trim().replace(/[/@.]+/g, '-').replace(/-+/g, '-');
if (!GROUP_NAME_RE.test(name)) {
  throw new TypeError(`Refusing to create group: "${rawInput}" sanitizes to invalid name "${name}"`);
}
await createGroupDir(gitnexusDir, name, force);

Type guard

function isValidGroupName(name: unknown): name is string {
  return typeof name === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(name);
}

Try / catch

try {
  await createGroupDir(gitnexusDir, name);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Invalid group name')) {
    // surface a friendly prompt for a corrected name; do not retry the same input
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling a group command (e.g. `gitnexus group create` or any API that goes through getGroupDir/createGroupDir) with a name like "my repo" (space), "../evil" or "org/team" (slash), "@org/team" (@), ".hidden" or "-flag" (leading non-alphanumeric), or a name containing dots, colons, or unicode characters.

Common situations: Scripting group creation from raw repository or workspace names that contain spaces or slashes; passing npm-style scoped names; a shell variable that picked up a leading dash or trailing whitespace; names copied from URLs.

Related errors


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

Appendix: source

Thrown at gitnexus/src/core/group/storage.ts:22

import type { ContractRegistry } from './types.js';
import { writeFileAtomic } from '../../storage/fs-atomic.js';
import { getGlobalDir } from '../../storage/global-dir.js';

export const CONTRACTS_FILE = 'contracts.json';

export function getDefaultGitnexusDir(): string {
  return getGlobalDir();
}

export function getGroupsBaseDir(gitnexusDir?: string): string {
  return path.join(gitnexusDir || getDefaultGitnexusDir(), 'groups');
}

const GROUP_NAME_RE = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;

export function validateGroupName(name: string): void {
  if (!GROUP_NAME_RE.test(name)) {
    throw new Error(
      `Invalid group name "${name}". Names must start with a letter or digit and contain only [a-zA-Z0-9_-].`,
    );
  }
}

export function getGroupDir(gitnexusDir: string, groupName: string): string {
  validateGroupName(groupName);
  return path.join(gitnexusDir, 'groups', groupName);
}

/** The registry path, so callers that stat or watch the file do not respell its name. */
export function getContractRegistryPath(groupDir: string): string {
  return path.join(groupDir, CONTRACTS_FILE);
}

export async function writeContractRegistry(
  groupDir: string,
  registry: ContractRegistry,

View on GitHub (pinned to 0d1aed942f)