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
- Rename the group to match [a-zA-Z0-9][a-zA-Z0-9_-]* (e.g. "my repo" -> "my-repo", "org/team" -> "org-team")
- Trim whitespace and strip/replace disallowed characters (slash, dot, @, spaces) in the script that generates the name
- 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
- Generate group names from a sanitize helper that trims and replaces disallowed characters before any group API call
- In CLIs, validate the name before touching the filesystem so users get input feedback, not a stack trace
- Never build group names from raw user input, repo URLs, or paths without normalization
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
- exceeds bytes
- must be a regular file
- "path" must be a string
- "path" must be an absolute path
- repository name must use only letters, digits, ".", "_", or…
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)