Summary
Four production sites in pkg/cli reimplement strings.Contains(err.Error(), "exit status 4") to detect gh-CLI authentication errors, while a centralized helper isPermissionError already exists at pkg/cli/audit.go:250. Duplicated, divergent substring checks make the auth-error contract fragile: each site may include different supporting substrings ("authentication required", "GH_TOKEN", "not logged into any GitHub hosts", etc.), so behavior can drift across commands.
Existing centralized helper
// pkg/cli/audit.go:250
func isPermissionError(err error) bool {
if err == nil {
return false
}
errStr := err.Error()
return strings.Contains(errStr, "authentication required") ||
strings.Contains(errStr, "exit status 4") ||
strings.Contains(errStr, "GitHub CLI authentication") ||
strings.Contains(errStr, "permission") ||
strings.Contains(errStr, "GH_TOKEN")
}
Call sites that should adopt the helper
| File:Line |
Current code |
Notes |
pkg/cli/logs_download.go:312 |
if strings.Contains(err.Error(), "exit status 4") |
Only checks one of the five substrings — narrower than the helper |
pkg/cli/logs_download.go:802 |
Same pattern, second site in same file |
|
pkg/cli/logs_github_api.go:286-291 |
Five substring checks inlined |
Has its own multi-substring policy that diverges from isPermissionError (e.g. checks "To use GitHub CLI in a GitHub Actions workflow" which the helper doesn't); reconcile and route through the helper |
Related narrower checks worth consolidating:
| File:Line |
Current code |
Suggested |
pkg/cli/secrets.go:113 |
if !strings.Contains(err.Error(), "403") |
Consider isPermissionError(err) or a sibling is403Error helper |
pkg/cli/mcp_secrets.go:49 |
if strings.Contains(err.Error(), "403") |
Same |
Why
- Helper-underadoption failure mode documented in prior Sergo runs (8/9/10/11) — pattern persistent across the codebase.
- Duplicate substring lists drift independently:
logs_github_api.go:286-291 already includes substrings the helper does not (auth-required messages specific to GitHub Actions workflow context).
- A single helper lets
enrichGHError evolve (e.g. switch to *exec.ExitError + ExitCode() extraction) once and benefit every consumer.
Suggested fix
- Move/widen
isPermissionError so its substring set is the union of what callers need (or split into composable helpers: isAuthError, is403Error).
- Replace the three direct sites above to call the helper.
- Optionally: in
enrichGHError (already at pkg/workflow/github_cli.go:81), extract the gh CLI exit code via errors.As(err, &exitErr) and store it on a structured error, so consumers can branch on errors.As instead of substring at all. Lower priority.
Validation
Severity
Medium — no user-visible bug today, but each new gh-CLI call site risks drifting from the contract, and the inline substring lists are already inconsistent across files.
Context
Found by Sergo Run 12 — helper-adoption follow-up + error-comparison audit. Run §25981825933.
Generated by 🤖 Sergo - Serena Go Expert · ● 23.5M · ◷
Summary
Four production sites in
pkg/clireimplementstrings.Contains(err.Error(), "exit status 4")to detect gh-CLI authentication errors, while a centralized helperisPermissionErroralready exists atpkg/cli/audit.go:250. Duplicated, divergent substring checks make the auth-error contract fragile: each site may include different supporting substrings ("authentication required","GH_TOKEN","not logged into any GitHub hosts", etc.), so behavior can drift across commands.Existing centralized helper
Call sites that should adopt the helper
pkg/cli/logs_download.go:312if strings.Contains(err.Error(), "exit status 4")pkg/cli/logs_download.go:802pkg/cli/logs_github_api.go:286-291isPermissionError(e.g. checks"To use GitHub CLI in a GitHub Actions workflow"which the helper doesn't); reconcile and route through the helperRelated narrower checks worth consolidating:
pkg/cli/secrets.go:113if !strings.Contains(err.Error(), "403")isPermissionError(err)or a siblingis403Errorhelperpkg/cli/mcp_secrets.go:49if strings.Contains(err.Error(), "403")Why
logs_github_api.go:286-291already includes substrings the helper does not (auth-required messages specific to GitHub Actions workflow context).enrichGHErrorevolve (e.g. switch to*exec.ExitError+ExitCode()extraction) once and benefit every consumer.Suggested fix
isPermissionErrorso its substring set is the union of what callers need (or split into composable helpers:isAuthError,is403Error).enrichGHError(already atpkg/workflow/github_cli.go:81), extract the gh CLI exit code viaerrors.As(err, &exitErr)and store it on a structured error, so consumers can branch onerrors.Asinstead of substring at all. Lower priority.Validation
go test ./pkg/cli/...passes.strings.Contains(err.Error(), "exit status 4")outside the helper.Severity
Medium — no user-visible bug today, but each new gh-CLI call site risks drifting from the contract, and the inline substring lists are already inconsistent across files.
Context
Found by Sergo Run 12 — helper-adoption follow-up + error-comparison audit. Run §25981825933.