[repository-quality] 🎯 Repository Quality Improvement Report - Error Handling Consistency & Panic Safety (2026-08-20) #54241
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
🎯 Repository Quality Improvement Report - Error Handling Consistency & Panic Safety
Analysis Date: 2026-08-20
Focus Area: Error Handling Consistency & Panic Safety in Library Code (Custom)
Strategy Type: Custom
Custom Area: Yes — the repo has a dedicated
errorfwrapv,panic-in-library-code, anderrstringmatchlinter already, but a codebase-wide sweep shows the linters are not catching every risky pattern (init-timesync.Oncepanics are exempted as "startup" and several hotspot files still concentrate un-wrappedfmt.Errorf(%v, err)calls that blockerrors.Is/errors.Ascomposition). This is a natural, high-leverage follow-up to yesterday's "Monolithic File Risk Reduction" run and complements the existing linter suite rather than duplicating it.Executive Summary
gh-awalready invests in static analysis for error hygiene (errorfwrapv,panic-in-library-code,errstringmatchcustom linters), which is a strength relative to most Go projects. However, a repository-wide grep sweep found meaningful residual risk: 919 of 2,504 non-testfmt.Errorfcall sites (~37%) still format an error with%vinstead of%w, concentrated in a handful of CLI hotspot files (init.go,dispatch.go,run_push.go,upgrade_command.go,audit.go). These call sites silently breakerrors.Is/errors.Aschains for callers and tests. Separately, severalpkg/workflowandpkg/actionpinsfiles usepanic()inside lazily-initialized (sync.Once) accessors for embedded JSON data — these are defensible as "should never happen in a released build" but currently rely only on thepanic-in-library-codelinter's exemption for init-like patterns rather than an explicit, documented contract, so a future refactor could unintentionally introduce a runtime panic path reachable from user input.Three fragile
strings.Contains(err.Error(), "...")checks were also found outside test/linter fixtures (project_command.go,update_extension_check.go,add_interactive_git.go), each currently exempted or unflagged, that would break silently if the wrapped CLI's (e.g.gh) error text changes. Recommended actions: (1) wrap the top-offending files' errors with%w, (2) add doc comments +nolintjustification consistency for the intentionalpanic()sites inpkg/workflow/pkg/actionpins, and (3) replace the three brittle string-matching error checks with structured detection helpers inpkg/cli/errorutil.Full Analysis Report
Focus Area: Error Handling Consistency & Panic Safety in Library Code
Current State Assessment
The codebase has custom golangci-lint-style analyzers (
pkg/linters/errorfwrapv,pkg/linters/panic-in-library-code,pkg/linters/errstringmatch) registered inpkg/linters/registry.go, indicating error-handling hygiene is already a recognized priority. Despite this, raw grep counts show substantial un-wrapped error formatting still in production code, and a small number ofpanic()calls inpkg/workflow/pkg/actionpinslazy-loaders that are not caught by the linter because they occur insync.Once/package-init-adjacent contexts.Metrics Collected:
fmt.Errorfcalls (non-test, non-linter)%wwrappingerrors.Newsentinel-style declarationserrors.Iserrors.Aspanic()calls outsidecmd/, tests, lintersstrings.Contains(err.Error(), ...)(non-test/linter)errorfwrapv,panic-in-library-code,errstringmatch)Findings
Strengths
%wwrapping, ban panics in library code, and flag brittle error-string matching — this is above-average investment in error-handling rigor.errors.Newsentinel usage (558 occurrences) shows an established pattern for comparable/typed errors.nolint:errstringmatchcomments show the team already documents deliberate exceptions (e.g.,add_interactive_git.go:22,errstringmatch.go:36) rather than silently suppressing.Areas for Improvement
fmt.Errorf(..., err)call sites lack%w, concentrated inpkg/cli/init.go(20),pkg/cli/dispatch.go(19),pkg/cli/run_push.go(18),pkg/workflow/compiler_orchestrator_engine.go(16),pkg/cli/upgrade_command.go(16). These breakerrors.Is/errors.Asfor calling code and tests that need to distinguish error types (e.g., "not found" vs "network" vs "auth" failures).pkg/workflow/model_aliases.go(lines 91, 148) andpkg/actionpins/data.go(line 67)panic()on failure to load embedded JSON data viasync.Once. These are defensible ("should never happen because the JSON ships with the binary") but are undocumented as an intentional invariant beyond a one-line comment, and are exempt frompanic-in-library-codeonly incidentally rather than via an explicit(nolint/redacted)with rationale, making future refactors risk accidentally invoking the "impossible" panic path with dynamic (non-embedded) data.pkg/cli/project_command.go:384andpkg/cli/update_extension_check.go:439usestrings.Contains(err.Error(), ...)without a(nolint/redacted):errstringmatchexplanation (unlike the already-documented case inadd_interactive_git.go:22), inconsistent with the project's own convention of justifying such exceptions.pkg/cli/gateway_logs_mcp.go:269has a stale// TODO: Implement token-usage correlation for MCP tool calls.with no tracking issue reference.Detailed Analysis
The
%w-wrapping gap matters most inpkg/cli, where CLI commands often need to distinguish "not found" vs "permission" vs "network" failures to print user-friendly messages or exit codes (errorutil.IsNotFoundErroris already used inproject_command.go:384, showing there's a real need for typed error introspection elsewhere too). Fixing the top 5 hotspot files (98 of the 919 non-wrapped sites, ~11%) would meaningfully raise the fraction of errors that supporterrors.Is/errors.Aswithout a repo-wide mechanical rewrite.The lazy-load
panic()sites inmodel_aliases.go,agentic_engine.go,permissions_toolset_data.go,pi_engine.go,mcp_setup_gateway.go, andmcp_setup_safe_outputs.goall follow the same shape: unmarshal embedded JSON once, panic on error. Because the JSON is(go/redacted):embed-ed, this can only fail if a future PR corrupts the embedded file — a build-time-detectable condition. Adding an explicit init-time self-check (e.g., a smallTestBuiltinDataIsValidtest already may exist, but it should be paired with a linter suppression comment and a one-line "documented panic contract" note, matching the pattern already demonstrated inpanic-in-library-code's own test fixture at line 86:// should not be flagged — documented panic contract).🤖 Tasks for Copilot Agent
NOTE TO PLANNER AGENT: Split the following tasks into individual work items.
Improvement Tasks
Task 1: Wrap errors with
%win top CLI hotspot filesPriority: Medium
Estimated Effort: Medium
Focus Area: Error Handling Consistency
Description: In
pkg/cli/init.go,pkg/cli/dispatch.go, andpkg/cli/run_push.go, convertfmt.Errorf("...: %v", err)(or similar non-%wformatting of anerrorargument) call sites to use%wso wrapped errors supporterrors.Is/errors.As. Do not change call sites where the underlying error type deliberately should not be exposed (e.g., logging-onlyPrintfcalls, which are notfmt.Errorfand should be left alone). Run the existingerrorfwrapvlinter (make lintor the project's linter target) after changes to confirm all remaining un-wrapped call sites in these three files are gone or justified with(nolint/redacted):errorfwrapvand a rationale comment.Acceptance Criteria:
fmt.Errorfcalls inpkg/cli/init.go,pkg/cli/dispatch.go,pkg/cli/run_push.gothat pass anerrorvalue use%winstead of%v/%s, unless justified with an inline(nolint/redacted):errorfwrapvcommenterrorfwrapvlinter passes clean on these three filesmake test-unit)Code Region:
pkg/cli/init.go,pkg/cli/dispatch.go,pkg/cli/run_push.goTask 2: Document the intentional panic contract in embedded-JSON lazy loaders
Priority: Low
Estimated Effort: Small
Focus Area: Panic Safety
Description: In
pkg/workflow/model_aliases.go(lines ~91 and ~148) andpkg/actionpins/data.go(~line 67), thepanic()calls fire only if the embedded ((go/redacted):embed) JSON data fails to unmarshal — a condition that should be impossible at runtime because the data ships inside the binary and is validated by existing tests. Add a doc comment directly above each panic call explaining this is a deliberate "should never happen" invariant guarded by build-time embedding and existing data-validation tests, matching the "documented panic contract" pattern already demonstrated inpkg/linters/panic-in-library-code/testdata/.../panicinlibrarycode.go:86. Verify a test exists (or add one if missing) that unmarshal-validates the actual embedded JSON files so a corrupt data file fails CI before shipping, not at runtime via panic.Acceptance Criteria:
model_aliases.gox2,data.gox1,agentic_engine.go,permissions_toolset_data.go,pi_engine.go,mcp_setup_gateway.go,mcp_setup_safe_outputs.go) has a one-line comment stating the panic is an intentional, build-time-guarded invariantCode Region:
pkg/workflow/model_aliases.go,pkg/actionpins/data.go,pkg/workflow/agentic_engine.go,pkg/workflow/permissions_toolset_data.go,pkg/workflow/pi_engine.go,pkg/workflow/mcp_setup_gateway.go,pkg/workflow/mcp_setup_safe_outputs.goTask 3: Justify or replace brittle
err.Error()string matchingPriority: Low
Estimated Effort: Small
Focus Area: Error Handling Consistency
Description:
pkg/cli/project_command.go:384andpkg/cli/update_extension_check.go:439usestrings.Contains(err.Error(), "...")to detect specific failure conditions from external command output (e.g., GitHub API/gh CLI text), which is flagged by the project's ownerrstringmatchlinter philosophy but currently lacks the(nolint/redacted):errstringmatchjustification comment used elsewhere (e.g.,pkg/cli/add_interactive_git.go:22). Either (a) replace the check with a structured error type/sentinel where the underlying call already returns one, or (b) if no structured alternative exists (e.g., wrapping an external CLI's free-text output), add a(nolint/redacted):errstringmatchcomment explaining why string matching is unavoidable, consistent with the existing convention.Acceptance Criteria:
pkg/cli/project_command.go:384andpkg/cli/update_extension_check.go:439either use structured error detection or carry a(nolint/redacted):errstringmatchcomment with rationaleerrstringmatchlinter run against these files shows no unexplained findingsCode Region:
pkg/cli/project_command.go:384,pkg/cli/update_extension_check.go:439Task 4: Resolve or track the stale token-usage correlation TODO
Priority: Low
Estimated Effort: Small
Focus Area: Code Quality
Description:
pkg/cli/gateway_logs_mcp.go:269contains// TODO: Implement token-usage correlation for MCP tool calls.with no linked tracking issue and no indication of current status. Investigate whether this feature is still needed; if so, replace the bare TODO with a reference to a tracked GitHub issue (create one if none exists) so the work is discoverable and prioritizable; if the functionality has since been implemented elsewhere or is no longer needed, remove the stale comment.Acceptance Criteria:
pkg/cli/gateway_logs_mcp.go:269TODO either references a tracked issue number or is removed as obsoleteCode Region:
pkg/cli/gateway_logs_mcp.go:269Investigate the TODO comment at pkg/cli/gateway_logs_mcp.go:269 ("TODO: Implement token-usage correlation for MCP tool calls."). Check whether this functionality has since been implemented elsewhere in the MCP gateway logging code (search pkg/workflow and pkg/cli for "token" and "correlation" near MCP log handling). If it's still an open gap, replace the bare TODO comment with a reference to a tracked GitHub issue (open a new issue describing the gap if one does not already exist) so the work is discoverable. If the feature is no longer relevant or has been implemented, remove the stale comment. Run `make fmt` after any changes.📊 Historical Context
Previous Focus Areas
🎯 Recommendations
Immediate Actions (This Week)
%win top 3 CLI hotspot files (init.go,dispatch.go,run_push.go) — Priority: MediumShort-term Actions (This Month)
err.Error()string-match checks — Priority: LowLong-term Actions (This Quarter)
errorfwrapvlinter's scope or running a repo-wide automated%wmigration pass across all ~919 remaining call sites — Priority: Low📈 Success Metrics
fmt.Errorfcall sites: 919 → <800 (after top-hotspot fixes)err.Error()checks with rationale: 1/3 → 3/3Next Steps
Generated by Repository Quality Improvement Agent
Next analysis: 2026-08-21 — Focus area selected by diversity algorithm
All reactions