{"record":{"id":"814d8360c74069d7","repo":"abhigyanpatwari/GitNexus","slug":"must-use-an-ssh-url-on-github-com-gitlab-com-or","errorCode":null,"errorMessage":"must use an SSH URL on github.com, gitlab.com, or gitee.com","messagePattern":"must use an SSH URL on github\\.com, gitlab\\.com, or gitee\\.com","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/auto-sync/config.ts","lineNumber":290,"sourceCode":"  return {\n    configPath,\n    syncIntervalMinutes: interval,\n    repoGitTimeoutMs,\n    analyzeTimeoutMs,\n    maxConcurrency,\n    analyzeFailureThreshold,\n    projects,\n  };\n}\n\nexport function validateAutoSyncRemoteUrl(remoteUrl: string): void {\n  const trimmed = remoteUrl.trim();\n  if (trimmed.includes('?') || trimmed.includes('#')) {\n    throw new Error('must not include query strings or fragments');\n  }\n  const match = /^git@([^:\\s/]+):([^\\s]+)$/.exec(trimmed);\n  if (!match) {\n    throw new Error('must use an SSH URL on github.com, gitlab.com, or gitee.com');\n  }\n  const host = match[1].toLowerCase();\n  const repoPath = match[2];\n  if (!ALLOWED_REMOTE_HOSTS.has(host)) {\n    throw new Error('host must be one of github.com, gitlab.com, or gitee.com');\n  }\n  const pathParts = repoPath.split('/');\n  // Every segment becomes a directory component: the namespace segments build\n  // the clone path and the last one names the repo. So each is held to the same\n  // charset, which is what keeps a separator out of a segment — on Windows\n  // `..\\..\\outside` is traversal even though the segment is not literally `..`,\n  // and testing the raw string for `..` instead would reject an ordinary\n  // `foo..bar`. Traversal is a whole segment; a separator is a character.\n  const namespaceParts = pathParts.slice(0, -1);\n  if (\n    repoPath.startsWith('/') ||\n    pathParts.length < 2 ||\n    pathParts.some((part) => !part || part === '.' || part === '..') ||","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/core/auto-sync/config.ts#L272-L308","documentation":"GitNexus auto-sync only accepts SSH-style remote URLs of the form git@host:owner/repo.git. This error is thrown by validateAutoSyncRemoteUrl when the configured remote_url does not match the required SSH regex at all — it is not HTTPS, not scp-like SSH syntax, or contains whitespace. The library throws it early (config parse / clone) to guarantee the downstream git clone command is unambiguous and safe.","triggerScenarios":"Calling parseAutoSyncConfig with a remote_url like https://github.com/owner/repo.git, or a git:// / ssh:// prefixed URL, or an SSH URL with a port (ssh://git@github.com:22/owner/repo.git), or a URL with spaces. Also thrown by extractRepoNameFromRemoteUrl, getAutoSyncRepoIdentity, and cloneOrPull when handed the same malformed value.","commonSituations":"Developer copies the HTTPS clone URL from the GitHub/GitLab 'Code' button instead of the SSH one; config written before SSH keys were set up; organization moved to self-hosted GitLab with a different hostname; URL pasted with a trailing newline or embedded spaces.","solutions":["Replace the remote URL with scp-like SSH syntax: git@github.com:owner/repo.git (colon, not slash, after host; no scheme).","Remove any scheme prefix (https://, ssh://) and any port segment — only git@host:path is accepted.","Confirm the host is github.com, gitlab.com, or gitee.com — a different host will pass this check but fail the next one with 'host must be one of...'.","Run git remote -v or `git ls-remote <url>` locally to confirm the URL works over SSH before putting it in the auto-sync config."],"exampleFix":"// before\nremote_url = https://github.com/acme/widgets.git\n// after\nremote_url = git@github.com:acme/widgets.git","handlingStrategy":"validation","validationCode":"function isValidSshRemoteUrl(url) {\n  const t = (url ?? '').trim();\n  if (t.includes('?') || t.includes('#')) return false;\n  return /^git@(github\\.com|gitlab\\.com|gitee\\.com):[\\w.-]+[\\/][\\w.\\/-]+\\.git$/.test(t);\n}\n// run before passing remote_url into the config\nif (!isValidSshRemoteUrl(cfg.remote_url)) throw new Error(`bad remote_url: ${cfg.remote_url}`);","typeGuard":"function isSshRemote(u) {\n  return typeof u === 'string' && /^git@[^:\\s/]+:\\S+$/.test(u.trim());\n}","tryCatchPattern":"try {\n  const cfg = parseAutoSyncConfig(raw);\n} catch (e) {\n  if (String(e.message).includes('must use an SSH URL')) {\n    log.error(`remote_url '${cfg?.remote_url}' must be git@host:owner/repo.git`);\n  }\n  throw e;\n}","preventionTips":["Always copy the SSH clone URL from the forge UI, never HTTPS.","Normalize URLs through one helper before writing config.","Keep the .git suffix; the library expects the standard scp-like form.","Lint auto-sync config files in CI with the same regex the library uses."],"tags":["config","git","url-format","validation"],"backgroundTag":"invalid-url-format","analyzedSha":"0d1aed942f0e8b5d3bac27519fff441aceea722d","analyzedAt":"2026-09-08T00:40:44.970Z","contentChangedAt":"2026-09-08T00:40:44.970Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}