{"record":{"id":"1f1a56bd9e9232b7","repo":"paperclipai/paperclip","slug":"invalid-github-url","errorCode":null,"errorMessage":"Invalid GitHub URL.","messagePattern":"Invalid GitHub URL\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"cli/src/commands/client/company.ts","lineNumber":914,"sourceCode":"      owner: owner!,\n      repo: repo!,\n      ref: ref || \"main\",\n      path: repoPath.join(\"/\"),\n    });\n  }\n\n  if (!looksLikeRepoUrl(trimmed)) {\n    throw new Error(\"GitHub source must be a GitHub or GitHub Enterprise URL, or owner/repo[/path] shorthand.\");\n  }\n  if (!ref) {\n    return trimmed;\n  }\n\n  const url = new URL(trimmed);\n  const hostname = url.hostname;\n  const parts = url.pathname.split(\"/\").filter(Boolean);\n  if (parts.length < 2) {\n    throw new Error(\"Invalid GitHub URL.\");\n  }\n\n  const owner = parts[0]!;\n  const repo = parts[1]!;\n  const existingPath = normalizeGithubImportPath(url.searchParams.get(\"path\"));\n  const existingCompanyPath = normalizeGithubImportPath(url.searchParams.get(\"companyPath\"));\n  if (existingCompanyPath) {\n    return buildGithubImportUrl({ hostname, owner, repo, ref, companyPath: existingCompanyPath });\n  }\n  if (existingPath) {\n    return buildGithubImportUrl({ hostname, owner, repo, ref, path: existingPath });\n  }\n  if (parts[2] === \"tree\") {\n    return buildGithubImportUrl({ hostname, owner, repo, ref, path: parts.slice(4).join(\"/\") });\n  }\n  if (parts[2] === \"blob\") {\n    return buildGithubImportUrl({ hostname, owner, repo, ref, companyPath: parts.slice(4).join(\"/\") });\n  }","sourceCodeStart":896,"sourceCodeEnd":932,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/cli/src/commands/client/company.ts#L896-L932","documentation":"Thrown by normalizeGithubImportSource after a URL passed looksLikeRepoUrl but its pathname has fewer than two non-empty segments. The function needs at least owner and repo segments; a URL with only a host (or host plus one segment) is considered structurally incomplete even though it parsed.","triggerScenarios":"A URL like 'https://github.com/' or 'https://github.com/acme' (only owner, no repo). A URL whose pathname collapses to empty after split/filter. Edge cases where looksLikeRepoUrl's first pass accepted a URL that fails the deeper segment check when a ref override is supplied.","commonSituations":"Truncated copy-paste of a repo URL. Typing the owner but forgetting the repo. A ref override (--ref) being applied to a malformed URL.","solutions":["Provide a full owner/repo URL: 'https://github.com/acme/widget'.","Verify the URL opens a repository in a browser before pasting it.","If using shorthand, ensure both owner and repo are present ('acme/widget', not 'acme').","Re-copy the URL from the GitHub repository page address bar."],"exampleFix":"# before\npaperclipai company import --source https://github.com/acme --ref v1\n# after\npaperclipai company import --source https://github.com/acme/widget --ref v1","handlingStrategy":"validation","validationCode":"function ensureGithubUrlHasOwnerRepo(source: string): void {\n  const u = new URL(source.trim());\n  const segments = u.pathname.split(\"/\").filter(Boolean);\n  if (segments.length < 2) {\n    throw new Error(`URL needs owner/repo segments: ${source}`);\n  }\n}","typeGuard":"function githubUrlHasOwnerRepo(input: string): boolean {\n  try {\n    const u = new URL(input.trim());\n    return u.pathname.split(\"/\").filter(Boolean).length >= 2;\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":null,"preventionTips":["Open the URL in a browser to confirm it lands on a repository page.","When using shorthand, type both owner and repo.","Reject one-segment URLs in upstream validation."],"tags":["cli","validation","github","url-parsing","company-import"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}