Conversation
There was a problem hiding this comment.
Pull request overview
Updates gh auth status to distinguish invalid credentials from network/verification failures in human-readable output while keeping the JSON schema unchanged.
Changes:
- Add error classification (
invalid_auth,network,unknown) and adjust error messaging/header text accordingly. - Extend tests to cover network failures and env-token invalid-token behavior, and update existing expectations for 401 “Bad credentials” cases.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| pkg/cmd/auth/status/status.go | Adds auth error classification and updates human-readable error output based on failure kind. |
| pkg/cmd/auth/status/status_test.go | Adds/updates test cases for network errors and invalid env tokens; adjusts expected outputs for 401 scenarios. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| reg.Register(httpmock.REST("GET", ""), func(req *http.Request) (*http.Response, error) { | ||
| return nil, &url.Error{ | ||
| Op: "Get", | ||
| URL: "https://api.github.com/", | ||
| Err: &net.DNSError{Err: "no such host", Name: "api.github.com"}, | ||
| } | ||
| }) |
There was a problem hiding this comment.
The test stub is returning a *url.Error from the mocked RoundTripper. net/http typically wraps transport errors in *url.Error itself, so returning a url.Error here can lead to double-wrapping and a brittle, duplicated error string in wantErrOut. Consider returning the underlying error (e.g., *net.DNSError) and letting http.Client construct the url.Error so the message format is stable and closer to real behavior.
| errMsg := err.Error() | ||
| if strings.Contains(errMsg, "401 Unauthorized") || strings.Contains(errMsg, "Bad credentials") { | ||
| return authEntryStateError, authFailureKindInvalidAuth | ||
| } | ||
|
|
There was a problem hiding this comment.
classifyAuthError falls back to substring matching on err.Error() ("401 Unauthorized" / "Bad credentials") to detect invalid auth. This is brittle and can misclassify unrelated errors or break if upstream error formatting changes. Prefer classifying based on concrete error types / status codes (e.g., handle go-gh's HTTP error type if it differs from api.HTTPError, or extract the status code via a typed interface) and avoid relying on error string contents.
| errMsg := err.Error() | |
| if strings.Contains(errMsg, "401 Unauthorized") || strings.Contains(errMsg, "Bad credentials") { | |
| return authEntryStateError, authFailureKindInvalidAuth | |
| } |
| reg.Register(httpmock.GraphQL(`query UserCurrent\b`), func(req *http.Request) (*http.Response, error) { | ||
| return nil, &url.Error{ | ||
| Op: "Post", | ||
| URL: "https://api.github.com/graphql", | ||
| Err: &net.DNSError{Err: "no such host", Name: "api.github.com"}, | ||
| } | ||
| }) |
There was a problem hiding this comment.
Same as the other network-error case: returning *url.Error from the mocked transport can cause net/http to wrap it again, producing a duplicated "Post ...: Post ..." error string that’s sensitive to implementation details. Returning the underlying *net.DNSError (and letting http.Client wrap) makes the expectation more stable.
| reg.Register(httpmock.REST("GET", ""), func(req *http.Request) (*http.Response, error) { | ||
| return nil, &url.Error{ | ||
| Op: "Get", | ||
| URL: "https://api.github.com/", | ||
| Err: &net.DNSError{Err: "no such host", Name: "api.github.com"}, | ||
| } | ||
| }) |
There was a problem hiding this comment.
This JSON-mode network error stub also returns *url.Error directly from the RoundTripper, which can lead to double-wrapped errors and brittle exact-string assertions. Consider returning the underlying error (e.g., *net.DNSError) and updating wantOut accordingly so the error string is less dependent on net/http internals.
|
Hi @edwardwc! 👋 This seems like a good idea, of course if there are valid use cases for it. So, as this needs more discussion on the rationale, I'm going to close the PR, as the PR is not yet in a reviewable stage. Can you please create an issue, explaining the rationale/use case? Please also cover more about your specific use case/situation to help us and the community better understand the need for this. |
|
Opened the follow-up issue requested here: #12891\n\nIssue link: https://github.com/cli/cli/issues/12891\n\nIt documents the rationale, concrete sandbox/network failure use case, and the reproduction details behind this PR. |
Summary
gh auth status currently reports that a token is invalid when the underlying auth check failed because the network request itself failed, for example in a sandboxed environment or during DNS resolution failures.
This change keeps the existing JSON output shape intact, but updates the human-readable status text to distinguish invalid credentials from transport-level verification failures for both stored tokens and environment tokens.
What Changed
Verification
env GOCACHE=/tmp/go-build GOMODCACHE=/tmp/gomodcache go test ./pkg/cmd/auth/status -count=1