{"record":{"id":"d49c561e34b417f7","repo":"abhigyanpatwari/GitNexus","slug":"host-must-be-one-of-github-com-gitlab-com-or-git","errorCode":null,"errorMessage":"host must be one of github.com, gitlab.com, or gitee.com","messagePattern":"host must be one of github\\.com, gitlab\\.com, or gitee\\.com","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"gitnexus/src/core/auto-sync/config.ts","lineNumber":295,"sourceCode":"    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 === '..') ||\n    namespaceParts.some((part) => !REMOTE_PATH_SEGMENT_PATTERN.test(part))\n  ) {\n    throw new Error('path must include owner/repo without traversal');\n  }\n  // The final segment becomes the on-disk clone directory via `extractRepoName`,","sourceCodeStart":277,"sourceCodeEnd":313,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/core/auto-sync/config.ts#L277-L313","documentation":"After the URL parses as SSH syntax, validateAutoSyncRemoteUrl checks the host portion against the allowlist ALLOWED_REMOTE_HOSTS (github.com, gitlab.com, gitee.com, case-insensitive). This error means the URL is valid SSH syntax but points at an unsupported host. Auto-sync only supports these three forges, so any other host is rejected before a clone is attempted.","triggerScenarios":"remote_url = git@bitbucket.org:acme/widgets.git, a self-hosted GitLab (git@gitlab.mycompany.com:acme/widgets.git), GitHub Enterprise (git@github.acme.io:...), or SSH URLs with a capitalized host that still resolves to a non-allowed host (host check is on the actual string, lowercased).","commonSituations":"Company hosts code on GitHub Enterprise or self-hosted GitLab; developer mistakenly configures a Bitbucket repo; mirror setups pointing at an internal git server.","solutions":["Move the repo to (or mirror it into) github.com, gitlab.com, or gitee.com and use that URL.","If you must keep the private host, exclude this project from auto-sync and sync it manually with git pull / the standard workflow.","Check for typos in the host (e.g. gitlab.cm instead of gitlab.com) that fall outside the allowlist.","Verify you are not accidentally including a port or subdomain in the host portion (git@github.com:22:... will not match)."],"exampleFix":"// before\nremote_url = git@gitlab.mycompany.com:acme/widgets.git\n// after\nremote_url = git@gitlab.com:acme/widgets.git","handlingStrategy":"validation","validationCode":"const ALLOWED = new Set(['github.com', 'gitlab.com', 'gitee.com']);\nfunction hostIsAllowed(url) {\n  const m = /^git@([^:\\s/]+):/.test(url) ;\n  if (!m) return false;\n  const host = url.trim().match(/^git@([^:\\s/]+):/)[1].toLowerCase();\n  return ALLOWED.has(host);\n}\nif (!hostIsAllowed(cfg.remote_url)) throw new Error('unsupported forge host');","typeGuard":"function isAllowedHost(u) {\n  const m = /^git@([^:\\s/]+):/.exec(typeof u === 'string' ? u.trim() : '');\n  return !!m && ['github.com','gitlab.com','gitee.com'].includes(m[1].toLowerCase());\n}","tryCatchPattern":"try {\n  validateAutoSyncRemoteUrl(remoteUrl);\n} catch (e) {\n  if (String(e.message).includes('host must be one of')) {\n    log.error(`Auto-sync only supports github.com, gitlab.com, gitee.com; got '${remoteUrl}'`);\n  }\n  throw e;\n}","preventionTips":["Before adopting auto-sync, confirm all repos live on one of the three supported forges.","Mirror enterprise repos to github.com/gitlab.com if auto-sync is required.","Validate hosts at config-authoring time with a shared allowlist constant."],"tags":["config","git","allowlist","validation"],"backgroundTag":"invalid-config-value","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"}