abhigyanpatwari/GitNexus · error · Error
path must include owner/repo without traversal
Error message
path must include owner/repo without traversal
What it means
This error is thrown when the repo path portion of an SSH remote URL is structurally invalid: it must contain at least two segments (owner and repo), must not start with '/', and no segment may be empty, '.', '..', or (for namespace segments) fail the remote path segment charset pattern. The rules exist because every segment becomes an on-disk directory component of the clone path — '..' or separator-bearing segments would enable path traversal outside the sync root.
Source
Thrown at gitnexus/src/core/auto-sync/config.ts:311
const repoPath = match[2];
if (!ALLOWED_REMOTE_HOSTS.has(host)) {
throw new Error('host must be one of github.com, gitlab.com, or gitee.com');
}
const pathParts = repoPath.split('/');
// Every segment becomes a directory component: the namespace segments build
// the clone path and the last one names the repo. So each is held to the same
// charset, which is what keeps a separator out of a segment — on Windows
// `..\..\outside` is traversal even though the segment is not literally `..`,
// and testing the raw string for `..` instead would reject an ordinary
// `foo..bar`. Traversal is a whole segment; a separator is a character.
const namespaceParts = pathParts.slice(0, -1);
if (
repoPath.startsWith('/') ||
pathParts.length < 2 ||
pathParts.some((part) => !part || part === '.' || part === '..') ||
namespaceParts.some((part) => !REMOTE_PATH_SEGMENT_PATTERN.test(part))
) {
throw new Error('path must include owner/repo without traversal');
}
// The final segment becomes the on-disk clone directory via `extractRepoName`,
// whose name rules are stricter than the path check above: a backslash — or
// anything outside `[A-Za-z0-9._-]` — passes here and then throws once per
// tick inside the sync loop instead of at config load. These rules are a
// strict superset, so anything accepted here is accepted there.
const lastSegment = pathParts[pathParts.length - 1];
const repoName = /\.git$/i.test(lastSegment) ? lastSegment.slice(0, -4) : lastSegment;
if (
!repoName ||
repoName === '.' ||
repoName === '..' ||
repoName === 'unknown' ||
repoName.startsWith('-') ||
!REMOTE_REPO_NAME_PATTERN.test(repoName)
) {
throw new Error(
'repository name must use only letters, digits, ".", "_", or "-" and must not be "unknown"',View on GitHub (pinned to 0d1aed942f)
Solutions
- Use the full owner/repo path exactly as the forge shows it: git@github.com:owner/repo.git.
- Remove any leading slash after the colon and any empty ('//') segments.
- Replace '.' or '..' segments with the real directory names — traversal segments are always rejected.
- If the repo lives in nested groups/subgroups, keep each group as its own segment: git@gitlab.com:group/subgroup/repo.git.
- Convert backslashes to forward slashes if the path was pasted from Windows.
Example fix
// before remote_url = git@github.com:acme/../outside/repo.git // after remote_url = git@github.com:acme/repo.git
Defensive patterns
Strategy: validation
Validate before calling
function pathLooksSafe(url) {
const m = /^git@[^:\s/]+:(\S+)$/.exec((url ?? '').trim());
if (!m) return false;
const parts = m[1].split('/');
if (m[1].startsWith('/') || parts.length < 2) return false;
return parts.every((p) => p && p !== '.' && p !== '..' && /^[A-Za-z0-9._-]+$/.test(p));
}
if (!pathLooksSafe(cfg.remote_url)) throw new Error('remote path must be owner/repo, no traversal'); Try / catch
try {
const cfg = parseAutoSyncConfig(raw);
} catch (e) {
if (String(e.message).includes('path must include owner/repo')) {
log.error('remote_url path must be owner/repo with no empty, ".", or ".." segments');
}
throw e;
} Prevention
- Always include owner and repo segments in the URL path.
- Never build remote URLs by string-concatenating filesystem paths.
- Reject '..' segments when generating config from user input.
- Use forward slashes only; backslashes will not survive segment validation.
When it happens
Trigger: remote_url = git@github.com:repo.git (no owner), git@github.com:/repo.git (leading slash), git@github.com:acme/../etc.git (traversal), git@github.com:acme//repo.git (empty segment), or a namespace segment containing characters outside the allowed segment pattern (e.g. spaces, backslashes like acme\\..\\outside).
Common situations: Hand-edited config where the owner was deleted; path pasted from a filesystem (Windows backslashes); intentionally deep-nested paths on a forge that supports subgroups with an extra or missing slash; malicious or corrupted config files.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- ${filename} resolves outside the repository root
- Path must not contain null bytes
- ${source} entry "${trimmed}" must be an identifier or member
- ${filename} must be a regular file
- must use an SSH URL on github.com, gitlab.com, or gitee.com
AI-assisted analysis of abhigyanpatwari/GitNexus@0d1aed942f (2026-09-08).
Data as JSON: /api/errors/52c5811786ef102f.
Report an issue: GitHub.