{"record":{"id":"0ac11ee9688bbf9e","repo":"google-gemini/gemini-cli","slug":"unable-to-find-any-remotes-for-repo-installmetad","errorCode":null,"errorMessage":"Unable to find any remotes for repo ${installMetadata.source}","messagePattern":"Unable to find any remotes for repo (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/config/extensions/github.ts","lineNumber":60,"sourceCode":"        if (\n          parsedUrl.protocol === 'https:' &&\n          parsedUrl.hostname === 'github.com'\n        ) {\n          if (!parsedUrl.username) {\n            parsedUrl.username = token;\n          }\n          sourceUrl = parsedUrl.toString();\n        }\n      } catch {\n        // If source is not a valid URL, we don't inject the token.\n        // We let git handle the source as is.\n      }\n    }\n    await git.clone(sourceUrl, './', ['--depth', '1']);\n\n    const remotes = await git.getRemotes(true);\n    if (remotes.length === 0) {\n      throw new Error(\n        `Unable to find any remotes for repo ${installMetadata.source}`,\n      );\n    }\n\n    const refToFetch = installMetadata.ref || 'HEAD';\n\n    await git.fetch(remotes[0].name, refToFetch);\n\n    // After fetching, checkout FETCH_HEAD to get the content of the fetched ref.\n    // This results in a detached HEAD state, which is fine for this purpose.\n    await git.checkout('FETCH_HEAD');\n  } catch (error) {\n    throw new Error(\n      `Failed to clone Git repository from ${installMetadata.source} ${getErrorMessage(error)}`,\n      {\n        cause: error,\n      },\n    );","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/cli/src/config/extensions/github.ts#L42-L78","documentation":"Thrown by cloneFromGit() after git.clone succeeds but git.getRemotes(true) returns an empty array. A normal clone registers an 'origin' remote, so zero remotes indicates the clone did not actually fetch a real repository (empty repo, intercepted clone, or a source that produced a working tree without remote config).","triggerScenarios":"cloneFromGit installs an extension whose source is an empty/private repo that clones but has no configured remote, or an environment where the clone is stubbed/mocked and returns no remote metadata.","commonSituations":"Private repo returning an empty clone due to partial auth; a mirror or bundled repo without remote config; CI with a git cache/shallow clone that strips remote metadata; flaky networks where getRemotes returns [] transiently.","solutions":["Verify the source repository is non-empty and the token (getGitHubToken) grants read access.","Retry the clone; transient empty-remote results can occur under network instability.","If maintaining a custom mirror, ensure an 'origin' remote is configured with git remote add."],"exampleFix":"// before\nconst remotes = await git.getRemotes(true);\nif (remotes.length === 0) throw new Error(...);\n// after\nconst remotes = await git.getRemotes(true);\nif (remotes.length === 0) {\n  await git.addRemote('origin', installMetadata.source);\n  remotes = await git.getRemotes(true);\n}","handlingStrategy":"retry","validationCode":"async function hasRemotes(dest: string): Promise<boolean> {\n  const g = simpleGit(dest); const r = await g.getRemotes(true); return r.length > 0;\n}","typeGuard":null,"tryCatchPattern":"try { await cloneFromGit(meta, dest); } catch (e) { if (/Unable to find any remotes/.test(String(e))) { await simpleGit(dest).addRemote('origin', meta.source); /* retry once */ await cloneFromGit(meta, dest); } else throw e; }","preventionTips":["Confirm the source repo is non-empty and reachable before cloning.","In tests, ensure the mocked clone registers an origin remote."],"tags":["extensions","git","network","installation"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}