{"record":{"id":"60193589ef29b474","repo":"google-gemini/gemini-cli","slug":"invalid-repo-url-source","errorCode":null,"errorMessage":"Invalid repo URL: ${source}","messagePattern":"Invalid repo URL: (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/cli/src/config/extensions/github.ts","lineNumber":106,"sourceCode":"  // Handle SCP-style SSH URLs.\n  if (source.startsWith('git@')) {\n    if (source.startsWith('git@github.com:')) {\n      // It's a GitHub SSH URL, so normalize it for the URL parser.\n      source = source.replace('git@github.com:', '');\n    } else {\n      // It's another provider's SSH URL (e.g., gitlab), so not a GitHub repo.\n      return null;\n    }\n  }\n  // Default to a github repo path, so `source` can be just an org/repo\n  let parsedUrl: URL;\n  try {\n    // Use the standard URL constructor for backward compatibility.\n    parsedUrl = new URL(source, 'https://github.com');\n  } catch (e) {\n    // Throw a TypeError to maintain a consistent error contract for invalid URLs.\n    // This avoids a breaking change for consumers who might expect a TypeError.\n    throw new TypeError(`Invalid repo URL: ${source}`, { cause: e });\n  }\n\n  if (!parsedUrl) {\n    throw new Error(`Invalid repo URL: ${source}`);\n  }\n  if (parsedUrl?.host !== 'github.com') {\n    return null;\n  }\n  // The pathname should be \"/owner/repo\".\n  const parts = parsedUrl?.pathname\n    .split('/')\n    // Remove the empty segments, fixes trailing and leading slashes\n    .filter((part) => part !== '');\n\n  if (parts?.length !== 2) {\n    throw new Error(\n      `Invalid GitHub repository source: ${source}. Expected \"owner/repo\" or a github repo uri.`,\n    );","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/cli/src/config/extensions/github.ts#L88-L124","documentation":"Thrown as a TypeError by tryParseGithubUrl() when new URL(source, 'https://github.com') throws — i.e., the source is so malformed it cannot be parsed even relative to the github base. The comment notes TypeError is used deliberately to keep a consistent error contract for consumers expecting invalid-URL failures.","triggerScenarios":"Calling tryParseGithubUrl with a source containing characters illegal in a URL (e.g., spaces, backslashes, certain control chars), or a scheme/structure the URL constructor rejects even with a base.","commonSituations":"User-typed source with a stray space or quote; copy-paste that includes surrounding markdown; a source read from a config file with trailing whitespace or BOM.","solutions":["Trim and validate the source string before calling tryParseGithubUrl.","Pass a well-formed 'owner/repo' shorthand or a full https/github URL.","Catch TypeError specifically if you iterate over candidate sources."],"exampleFix":"// before\nconst info = tryParseGithubUrl(rawSource);\n// after\nconst trimmed = rawSource.trim();\nif (!/^[^\\s]+$/.test(trimmed)) throw new Error('source must not contain whitespace');\nconst info = tryParseGithubUrl(trimmed);","handlingStrategy":"validation","validationCode":"function isValidSourceToken(source: string): boolean {\n  const s = source.trim();\n  return s.length > 0 && !/\\s/.test(s);\n}","typeGuard":"function isParsableUrl(source: string, base = 'https://github.com'): boolean { try { new URL(source, base); return true; } catch { return false; } }","tryCatchPattern":"try { tryParseGithubUrl(src); } catch (e) { if (e instanceof TypeError) { /* skip invalid candidate */ } else throw e; }","preventionTips":["Trim candidate sources and reject whitespace before parsing.","When iterating candidate sources, catch TypeError to skip rather than abort."],"tags":["extensions","git","validation","url-parsing"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}