@@ -255,6 +255,42 @@ def _reset_repo_dir(repo_dir: Path) -> None:
255255 shutil .rmtree (repo_dir , ignore_errors = True )
256256
257257
258+ def _is_explicit_auth_failure (lowered : str ) -> bool :
259+ """Detect explicit authentication failures from git credential helpers."""
260+ return "authentication failed" in lowered or "permission denied" in lowered
261+
262+
263+ def _is_repo_not_found (lowered : str ) -> bool :
264+ """Detect explicit 'repository not found' responses from the server."""
265+ return (
266+ "repository not found" in lowered
267+ or ("not found" in lowered and "repository" in lowered )
268+ or "does not exist" in lowered
269+ )
270+
271+
272+ def _is_network_error (lowered : str ) -> bool :
273+ """Detect DNS / network failures."""
274+ return "could not resolve host" in lowered
275+
276+
277+ def _is_ambiguous_auth_hint (lowered : str ) -> bool :
278+ """Detect ambiguous errors that likely indicate missing authentication.
279+
280+ When no GitHub token is set, these errors suggest the repo is private
281+ or doesn't exist. Includes the empty-message case — git sometimes
282+ exits non-zero with no output when auth is needed.
283+ """
284+ return (
285+ not lowered
286+ or "could not read username" in lowered # no credential helper
287+ or "terminal prompts disabled" in lowered # GIT_TERMINAL_PROMPT=0
288+ or "authentication required" in lowered
289+ or "authorization failed" in lowered
290+ or "access denied" in lowered
291+ )
292+
293+
258294def _raise_clone_error (
259295 stderr : str | None ,
260296 owner : str ,
@@ -279,7 +315,7 @@ def _raise_clone_error(
279315 token_missing = _is_github_source (source ) and not get_github_token ()
280316
281317 # 1. Explicit authentication failures (git credential helper responded)
282- if "authentication failed" in lowered or "permission denied" in lowered :
318+ if _is_explicit_auth_failure ( lowered ) :
283319 if token_missing :
284320 raise AuthenticationError (
285321 f"Authentication failed for source '{ source .name } '. "
@@ -290,33 +326,21 @@ def _raise_clone_error(
290326 ) from None
291327
292328 # 2. Explicit "not found" responses from the server
293- if (
294- "repository not found" in lowered
295- or ("not found" in lowered and "repository" in lowered )
296- or "does not exist" in lowered
297- ):
329+ if _is_repo_not_found (lowered ):
298330 raise RepoNotFoundError (
299331 f"Repository '{ owner } /{ repo_name } ' not found in source '{ source .name } '."
300332 ) from None
301333
302334 # 3. DNS / network failures
303- if "could not resolve host" in lowered :
335+ if _is_network_error ( lowered ) :
304336 raise AgrError (
305337 f"Network error: could not resolve host for source '{ source .name } '."
306338 ) from None
307339
308340 # 4. Heuristic: when no token is set, ambiguous errors likely mean the
309341 # repo is private or doesn't exist. We report "not found" to guide the
310- # user toward setting GITHUB_TOKEN. This includes empty stderr (git
311- # sometimes exits non-zero with no output when auth is needed).
312- if token_missing and (
313- not lowered
314- or "could not read username" in lowered # no credential helper
315- or "terminal prompts disabled" in lowered # GIT_TERMINAL_PROMPT=0
316- or "authentication required" in lowered
317- or "authorization failed" in lowered
318- or "access denied" in lowered
319- ):
342+ # user toward setting GITHUB_TOKEN.
343+ if token_missing and _is_ambiguous_auth_hint (lowered ):
320344 raise RepoNotFoundError (
321345 f"Repository '{ owner } /{ repo_name } ' not found in source '{ source .name } '."
322346 ) from None
0 commit comments