abhigyanpatwari/GitNexus · error

Could not extract a valid repository name from URL

Error message

Could not extract a valid repository name from URL

What it means

extractRepoName takes the last path segment of a git URL (via parseRepoNameFromUrl) and requires a filesystem-safe name matching ^[a-zA-Z0-9._-]+$, additionally rejecting '.', '..', and the 'unknown' sentinel. Anything else throws, because getCloneDir would otherwise let names like '..' or 'foo/bar' escape the clone root via path traversal.

Solutions

  1. Send the canonical HTTPS URL ending in the repo slug, optionally with .git
  2. Ensure the repo slug is pure ASCII alphanumerics plus . _ - (rename or alias non-ASCII slugs)
  3. Trim whitespace and strip fragments/queries from user-pasted URLs before submitting

Example fix

// before
const url = 'https://github.com/user/my repo';
extractRepoName(url); // throws

// after
const url = 'https://github.com/user/my-repo';
extractRepoName(url); // 'my-repo'
Defensive patterns

Strategy: validation

Validate before calling

const SAFE_NAME = /^[a-zA-Z0-9._-]+$/;
function lastSegment(url) { return new URL(url).pathname.replace(/\/+$/, '').split('/').pop() || ''; }
function urlYieldsSafeRepoName(url) {
  try { const n = lastSegment(url).replace(/\.git$/, ''); return SAFE_NAME.test(n) && n !== '.' && n !== '..' && n !== 'unknown'; }
  catch { return false; }
}

Type guard

function isSafeRepoName(name) {
  return typeof name === 'string' && name.length > 0 && name !== '.' && name !== '..' && name !== 'unknown' && /^[a-zA-Z0-9._-]+$/.test(name);
}

Try / catch

try { targetPath = getCloneDir(extractRepoName(url)); }
catch (e) {
  if (e.message === 'Could not extract a valid repository name from URL') throw new BadRequest(`unsupported repo url: ${url}`, 400);
  throw e;
}

Prevention

When it happens

Trigger: POST /api/analyze (or any cloneOrPull caller) with a URL whose final segment is empty ('https://github.com/user/') or contains characters outside the safe set — spaces ('my repo'), CJK/unicode, backslashes, or shell metacharacters ('repo;ls').

Common situations: Trailing-slash or bare-host URLs; repositories with human-display names containing spaces/unicode; pasted URLs with a trailing fragment or whitespace; URLs where the repo slug itself is non-ASCII.

Related errors


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

Appendix: source

Thrown at gitnexus/src/server/git-clone.ts:63

 *
 * Throws if the URL does not yield a filesystem-safe last segment. A name
 * like `..` or `foo/bar` would otherwise let `getCloneDir(name)` escape the
 * clone root via path traversal.
 */
export function extractRepoName(url: string): string {
  let trimmed = url.trim();
  while (trimmed.endsWith('/')) trimmed = trimmed.slice(0, -1);
  const withoutGit = trimmed.toLowerCase().endsWith('.git') ? trimmed.slice(0, -4) : trimmed;
  const name = withoutGit.split(/[/:]/).filter(Boolean).pop() ?? '';
  if (
    !name ||
    name === '.' ||
    name === '..' ||
    name === 'unknown' ||
    name.startsWith('-') ||
    !REPO_NAME_PATTERN.test(name)
  ) {
    throw new Error('Could not extract a valid repository name from URL');
  }
  return name;
}

/**
 * Derive a clone directory name for the web `/api/analyze` boundary.
 *
 * The API historically accepted Azure DevOps and similar URLs whose repo
 * segment contains spaces or other directory-unsafe characters by sanitizing
 * the final segment. Keep that compatibility at the web boundary while leaving
 * `extractRepoName()` strict for internal/security-sensitive callers.
 */
export function extractWebRepoName(url: string): string {
  let trimmed = url.trim();
  while (trimmed.endsWith('/')) trimmed = trimmed.slice(0, -1);
  const withoutGit = trimmed.toLowerCase().endsWith('.git') ? trimmed.slice(0, -4) : trimmed;
  const rawName = withoutGit.split(/[/:]/).filter(Boolean).pop() ?? '';
  const safeName = sanitizeRepoName(rawName);

View on GitHub (pinned to ac9a4e9abd)