Describe the bug
AuthResolver.is_public_github_auth_failure classifies a failed clone by matching English signal strings against git's stderr ("authentication failed", "could not read username", "repository not found", ...). Git localises those diagnostics through gettext, and APM does not pin a locale for the git subprocesses it spawns itself.
On a machine whose locale is not English, git writes its authentication failure in the user's language. The classifier does not recognise it, and the consequence is not cosmetic: the verdict gates the token retry in try_with_fallback (src/apm_cli/core/auth.py:797-802).
if (
lazy_public_github
and path is not None
and not self.is_public_github_auth_failure(exc)
):
raise
ctx = _auth_context()
if ctx.token:
_log(f"Unauthenticated failed, retrying with token (source: {ctx.source})")
Since uses_public_github_anonymous_first makes every github.com access start anonymously, a misclassified failure means the token is never tried. Installing a private-repo dependency fails, and the user is pointed at the wrong cause:
Could not connect to github.com (network error, not an auth failure).
Check your internet connection and proxy settings.
while git actually said "authentication failed".
Two things make this hard to spot:
- Clones issued through GitPython are unaffected, because GitPython forces
LANGUAGE=C/LC_ALL=C on every command it runs (git/cmd.py). Only APM's own subprocess calls inherit the ambient locale — and that is the path the shared bare cache uses.
- Git ships message catalogues for 19 languages today. A user whose language has no catalogue (e.g.
ja) sees English and is unaffected — until that translation is added.
To Reproduce
- Set a locale for which git ships a catalogue:
export LANG=fr_FR.UTF-8 (de_DE.UTF-8 behaves identically).
- Have a token available through
gh auth login, but not exported in the environment, so it has to come from the retry path.
- Use a cold cache:
export APM_CACHE_DIR=$(mktemp -d).
apm install <org>/<private-repo>/<subdir> — a subdirectory package, which is the case that routes through the shared bare-cache clone.
Same command, same token, same repository; only the locale changes:
LC_ALL |
Result |
fr_FR.UTF-8 |
fails — "network error, not an auth failure" |
de_DE.UTF-8 |
fails — same |
C |
installs successfully |
LC_ALL=C apm install ... alone makes the install succeed with no code change, which isolates the locale as the cause.
The exception captured on the failing path (values redacted):
CalledProcessError: Command '[... 'clone', '--bare', 'https://github.com/<org>/<repo>.git', ...]'
returned non-zero exit status 128.
stderr: "remote: Invalid username or token. Password authentication is not supported
for Git operations.
fatal : Échec d'authentification pour 'https://github.com/<org>/<repo>.git/'"
is_public_github_auth_failure returns False for it, so try_with_fallback re-raises without trying the token. Instrumenting the same run under LC_ALL=C yields the untranslated fatal: Authentication failed, a True verdict, and a successful token retry.
Expected behavior
Classification of an authentication failure should not depend on the user's locale. A private-repo dependency should install regardless of the language configured on the machine, and a genuine auth failure should be reported as such instead of as a network/proxy problem.
Environment (please complete the following information):
- OS: macOS (
darwin); reproducible on any non-English locale
- Python Version: 3.12
- APM Version: 0.28.0 (also on
main @ 3aa0365)
- git: 2.51.0, shipping catalogues for 19 languages under
share/locale/
Additional context
How this surfaced: a shared agent rule hosted in a subdirectory of a private repository. The repository root installed fine, the subdirectory package did not — the subdirectory is what routes through the shared bare cache, whose subprocess calls carry the ambient locale. The subdirectory is the trigger, not the cause. The error message sent me looking at proxy and connectivity settings for what was a credential-resolution problem.
I have a fix and will open a PR referencing this issue: normalise the message locale in AuthResolver._build_git_env, the single builder every git env in the auth path routes through. Widening the classifier's vocabulary with translated strings looks like the wrong direction — the catalogues change between git versions and cover a moving set of languages.
Describe the bug
AuthResolver.is_public_github_auth_failureclassifies a failed clone by matching English signal strings against git's stderr ("authentication failed","could not read username","repository not found", ...). Git localises those diagnostics through gettext, and APM does not pin a locale for the git subprocesses it spawns itself.On a machine whose locale is not English, git writes its authentication failure in the user's language. The classifier does not recognise it, and the consequence is not cosmetic: the verdict gates the token retry in
try_with_fallback(src/apm_cli/core/auth.py:797-802).Since
uses_public_github_anonymous_firstmakes every github.com access start anonymously, a misclassified failure means the token is never tried. Installing a private-repo dependency fails, and the user is pointed at the wrong cause:while git actually said "authentication failed".
Two things make this hard to spot:
LANGUAGE=C/LC_ALL=Con every command it runs (git/cmd.py). Only APM's ownsubprocesscalls inherit the ambient locale — and that is the path the shared bare cache uses.ja) sees English and is unaffected — until that translation is added.To Reproduce
export LANG=fr_FR.UTF-8(de_DE.UTF-8behaves identically).gh auth login, but not exported in the environment, so it has to come from the retry path.export APM_CACHE_DIR=$(mktemp -d).apm install <org>/<private-repo>/<subdir>— a subdirectory package, which is the case that routes through the shared bare-cache clone.Same command, same token, same repository; only the locale changes:
LC_ALLfr_FR.UTF-8de_DE.UTF-8CLC_ALL=C apm install ...alone makes the install succeed with no code change, which isolates the locale as the cause.The exception captured on the failing path (values redacted):
is_public_github_auth_failurereturnsFalsefor it, sotry_with_fallbackre-raises without trying the token. Instrumenting the same run underLC_ALL=Cyields the untranslatedfatal: Authentication failed, aTrueverdict, and a successful token retry.Expected behavior
Classification of an authentication failure should not depend on the user's locale. A private-repo dependency should install regardless of the language configured on the machine, and a genuine auth failure should be reported as such instead of as a network/proxy problem.
Environment (please complete the following information):
darwin); reproducible on any non-English localemain@3aa0365)share/locale/Additional context
How this surfaced: a shared agent rule hosted in a subdirectory of a private repository. The repository root installed fine, the subdirectory package did not — the subdirectory is what routes through the shared bare cache, whose
subprocesscalls carry the ambient locale. The subdirectory is the trigger, not the cause. The error message sent me looking at proxy and connectivity settings for what was a credential-resolution problem.I have a fix and will open a PR referencing this issue: normalise the message locale in
AuthResolver._build_git_env, the single builder every git env in the auth path routes through. Widening the classifier's vocabulary with translated strings looks like the wrong direction — the catalogues change between git versions and cover a moving set of languages.