fix(claude-code): parse auth status JSON before exit code; use authMethod (#1260)#1302
Conversation
…thod (Tracer-Cloud#1260) Follow-up to Tracer-Cloud#1295. Two cases of `claude auth status` were misclassified: 1. No auth + no env key → CLI exits 1 with valid JSON `{"loggedIn": false, "authMethod": "none"}`. The probe short-circuited on `returncode != 0` before parsing JSON and returned `None`, so the wizard surfaced "could not verify Claude Code CLI login" instead of the correct "Claude Code CLI requires login" — same logged_in=None surface Tracer-Cloud#1199 was about, just without the env-key fallback to rescue it. 2. Subscription + ANTHROPIC_API_KEY in env → CLI emits both `authMethod: "claude.ai"` and `apiKeySource: "ANTHROPIC_API_KEY"`. The prior code branched on `apiKeySource`-presence and reported "Authenticated via ANTHROPIC_API_KEY" even though the active method was the subscription. `authMethod` (`claude.ai` / `api_key` / `none`) is the authoritative discriminator; `apiKeySource` is set whenever the env contributes a key, regardless of which method is in use. Implementation: - Extract `_auth_status_from_json_payload` and `_try_parse_auth_status_stdout` so the JSON path is testable in isolation. - `_probe_cli_auth` now parses stdout first; the exit-code error path is a fallback only when JSON is absent or unparseable. - Detail-string branching uses `authMethod` first; falls back to `apiKeySource` / `email` for older CLI versions that omit `authMethod`. Live verification on claude 2.1.123 (all four states): - subscription only → "Authenticated via Claude subscription (...)" - api key only → "Authenticated via ANTHROPIC_API_KEY." - subscription + env API key → "Authenticated via Claude subscription (...)" (was: "via ANTHROPIC_API_KEY") - no auth (fresh HOME) → (False, "Not authenticated. ...") (was: (None, "claude auth status failed: ...")) Adds two regression tests: - test_probe_cli_auth_not_logged_in_exits_1 — exit 1 + valid JSON loggedIn:false - test_probe_cli_auth_subscription_with_env_api_key_reports_subscription - test_probe_cli_auth_api_key_only_uses_authmethod (covers the api_key branch) make lint / format-check / typecheck clean. make test-cov: 4967 passed.
Greptile Summary
Confidence Score: 5/5Safe to merge — both bugs are correctly fixed, all edge cases are handled, and regression tests pin the new behavior. No P0 or P1 issues found. The logic reordering is correct (JSON parse before exit-code check), the authMethod-first branching eliminates the mis-reporting, the legacy fallback is preserved for older CLI versions, and both previously flagged gaps (unrecognized authMethod fallthrough, missing stderr-only test) are now addressed. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A([_probe_cli_auth]) --> B[subprocess.run 'claude auth status']
B --> C{OSError / Timeout?}
C -->|Yes| D[Return None, error message]
C -->|No| E[_try_parse_auth_status_stdout stdout]
E --> F{Valid JSON dict?}
F -->|Yes| G[_auth_status_from_json_payload]
F -->|No - empty / invalid / non-dict| H{returncode != 0?}
H -->|Yes| I[Return None, 'claude auth status failed']
H -->|No| J{Negative markers in plain text?}
J -->|Yes| K[Return False, 'Not authenticated']
J -->|No| L[Return True, 'Authenticated via Claude CLI']
G --> M{loggedIn falsy?}
M -->|Yes| N[Return False, 'Not authenticated']
M -->|No| O{authMethod == api_key?}
O -->|Yes| P[Return True, via apiKeySource]
O -->|No| Q{authMethod == claude.ai?}
Q -->|Yes| R[Return True, via subscription + email]
Q -->|No| S{authMethod non-empty?}
S -->|Yes| T[Return True, via authMethod verbatim + email]
S -->|No - legacy fallback| U{apiKeySource present?}
U -->|Yes| V[Return True, via apiKeySource]
U -->|No| W{email present?}
W -->|Yes| X[Return True, via subscription + email]
W -->|No| Y[Return True, Authenticated via Claude CLI]
Reviews (2): Last reviewed commit: "fix(claude-code): guard unrecognized aut..." | Re-trigger Greptile |
|
good work. review fixes and good to merge 👍 |
Future authMethod values (e.g. oauth/sso) emitted alongside a populated apiKeySource were skipping both explicit branches and landing on the legacy heuristic, mis-reporting the env var as the active source — the same kind of mis-attribution this PR fixes for claude.ai. Surface the authMethod verbatim instead. Adds the missing stderr-only-JSON contract test so a future stderr-parsing refactor cannot silently change _probe_cli_auth's return value.
|
@greptileai review |
|
🧠 @Davidson3556 opened a PR. Maintainers feared them. CI genuflected. It merged. 🚨 👋 Join us on Discord - OpenSRE : hang out, contribute, or hunt for features and issues. Everyone's welcome. |

Fixes #1260 (follow-up to #1295)
Describe the changes you have made in this PR -
Two bugs in
_probe_cli_authsurfaced by running the liveclaude auth statusagainst every documented auth state. Both still onmainafter #1295 merged.1. No auth + no env key — wizard says "could not verify" instead of "requires login"
The CLI exits 1 with valid JSON
{\"loggedIn\": false, \"authMethod\": \"none\", ...}when no auth is configured. The probe short-circuited onreturncode != 0before parsing JSON and returnedNone, so the wizard took thelogged_in is Nonebranch ("could not verify") instead of theFalsebranch ("requires login"). Users had no signal that they needed to runclaude login— the samelogged_in=NoneUX surface that #1199 originally highlighted.2. Subscription +
ANTHROPIC_API_KEYin env — detail string misreports sourceThe CLI emits both
authMethod: \"claude.ai\"andapiKeySource: \"ANTHROPIC_API_KEY\"when a subscription user also has the env var set, but the active method is the subscription. The prior code branched onapiKeySource-presence and reported "Authenticated via ANTHROPIC_API_KEY".authMethod(claude.ai/api_key/none) is the authoritative discriminator;apiKeySourceis set whenever the env contributes a key, regardless of which method is active.Implementation
_auth_status_from_json_payloadand_try_parse_auth_status_stdoutso the JSON path is testable in isolation._probe_cli_authnow parses stdout first; the exit-code error path is a fallback only when JSON is absent or unparseable.authMethodfirst; falls back toapiKeySource/emailfor older CLI versions that omitauthMethod.Demo / verification
Live against
claude2.1.123 across all four auth states:(True, \"...subscription (email)\")(True, \"...ANTHROPIC_API_KEY\")(True, \"...ANTHROPIC_API_KEY\")❌(True, \"...subscription (email)\")✅(None, \"claude auth status failed: ...\")❌(False, \"Not authenticated. ...\")✅Reproduce the no-auth case in isolation (doesn't touch your real auth):
```bash
env -i HOME=/tmp/empty PATH=$PATH NO_COLOR=1 claude auth status; echo "exit: $?"
{"loggedIn": false, "authMethod": "none", "apiProvider": "firstParty"}
exit: 1
```
Tests
make lint✅make format-check✅make typecheck✅make test-cov→ 4967 passedtest_probe_cli_auth_not_logged_in_exits_1— exit 1 + valid JSONloggedIn:falsetest_probe_cli_auth_subscription_with_env_api_key_reports_subscriptiontest_probe_cli_auth_api_key_only_uses_authmethodCode Understanding and AI Usage
Did you use AI assistance?
If you used AI assistance:
Explain your implementation approach:
I found these two bugs by running
claude auth statusdirectly against the live CLI in each of the four documented auth states (subscription only, API key only, subscription + env key, no auth) and recording the exact JSON shape and exit code in each case. The third row (subscription + env key) showed the CLI emittingapiKeySourceeven whenauthMethodwasclaude.ai— that provesapiKeySource-presence is the wrong discriminator. The fourth row showed exit 1 with valid JSON, which the existing probe was treating as an opaque failure.The fix has two parts. (1) Reorder
_probe_cli_authso JSON parsing happens before the exit-code check — the helper_try_parse_auth_status_stdoutreturnsNoneonly when stdout is genuinely not a JSON object, in which case the exit code becomes the next signal. (2) In the JSON-payload mapper, branch onauthMethodfirst; only fall through to the legacyapiKeySource/emailheuristic whenauthMethodis missing, which preserves backward compat with older CLI versions that don't emit it. I extracted the helpers so the JSON path is testable without spinning up a subprocess mock.Edge cases I checked:
auth status(exit non-zero, non-JSON stderr) → still returnsNone(probe failure)._try_parse_auth_status_stdoutreturnsNone→ exit code path takes over.[]) →_try_parse_auth_status_stdoutreturnsNone, same fallback.loggedIn: truewith noauthMethodand noapiKeySourceand noemail→ "Authenticated via Claude CLI." (legacy fallback).Checklist before requesting a review