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
- Send the canonical HTTPS URL ending in the repo slug, optionally with .git
- Ensure the repo slug is pure ASCII alphanumerics plus . _ - (rename or alias non-ASCII slugs)
- 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
- Normalize URLs client-side: trim, strip fragments/queries, drop trailing slashes
- Reject or transliterate non-ASCII repo slugs before submission
- Keep repo names to [A-Za-z0-9._-] by policy
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
- Invalid URL
- Invalid repository name
- Only https:// and http:// git URLs are allowed
- must not be blank.
- argument contains a double quote, unsafe for the Windows…
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)