[repository-quality] Repository Quality Improvement Report — Error Chain Transparency & Non-Wrapping fmt.Errorf Gap #46253
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 Chain Transparency & Non-Wrapping fmt.Errorf Gap
Analysis Date: 2026-07-17
Focus Area: Error Chain Transparency & Non-Wrapping fmt.Errorf Gap
Strategy Type: Custom
Custom Area: Yes — the codebase has a well-developed custom error type hierarchy (15 named error structs) and
errors.Is/errors.Asusage (121 callsites), yet 788 non-wrappingfmt.Errorfcalls (37% of allfmt.Errorf) silently discard the upstream cause, making programmatic error classification brittle and stacktraces opaque.Executive Summary
gh-aw has a mature structured error story: 15 distinct error types (
WorkflowValidationError,ImportError,CompilerError,GuidedError, etc.), 121errors.Is/errors.Ascallsites, and theerrorfwrapvlinter that catches%v-instead-of-%wmisuse. Despite this foundation, 788fmt.Errorfcalls across 395 production files use no error argument at all — they create brand-new leaf errors that can never be inspected viaerrors.Is/errors.As. The three busiest packages account for 754/788 (96%):pkg/workflow(375),pkg/cli(286), andpkg/parser(93).The bulk of these are validation errors that format user-visible messages. Those errors intentionally discard upstream cause because there is no upstream cause — they are leaf errors. However a large subset occurs in parser and compiler paths where an upstream structured error does exist but is swallowed by a fresh
fmt.Errorf. Additionally, the repository has zero uses ofgolang.org/x/sync/errgroupdespite 8 fire-and-forget goroutines, 4 of which (docker_images.go,bootstrap_profile_runner.go,mcp_inspect_inspector.go,update_check.go) could benefit from structured concurrency.Five tasks below address the highest-value fixes: wrapping audit in the parser import pipeline,
errgroupadoption, a linter to detect discarded-cause patterns, sentinel error promotion for the most re-matched strings, and cleanup of thestrings.Contains(err.Error(), ...)suppressions.Full Analysis Report
Focus Area: Error Chain Transparency & Non-Wrapping fmt.Errorf Gap
Current State Assessment
The codebase uses
fmt.Errorfin two distinct modes:%wverb): 1,453 callsites — wraps an upstream error soerrors.Is/errors.Ascan inspect it.%wverb): 788 callsites — creates a new leaf error; any upstream cause is silently discarded.The
errorfwrapvlinter catches the middle case (%von an error argument), but it cannot flag callsites that have no error argument — those are syntactically valid leaf errors that may be missing a%wclause by accident.Metrics Collected:
fmt.Errorfwith%w(wrapping)fmt.Errorfwithout%w(non-wrapping)errors.Is/errors.Ascallsites (prod)var ErrXxx = errors.Newstrings.Contains(err.Error(), ...)in productiongolang.org/x/sync/errgroupusagesgo func()goroutinesFindings
Strengths
Error()/Unwrap()implementations.errorfwrapvlinter prevents%v-on-error anti-pattern.errstringmatchlinter flags brittlestrings.Contains(err.Error(), ...)— though 4 production suppressions remain.Areas for Improvement
pkg/parser/import_schema_validation.go(11 non-wrapping),pkg/parser/schedule_parser.go(13),pkg/parser/import_field_extractor.go— these parse structured YAML and likely receive upstream YAML errors that are currently discarded.strings.Contains(err.Error(), ...)production suppressions bypasserrstringmatch; 2 could be replaced with typed sentinel comparisons (errors.Is).var ErrXxxvars; the most frequently re-matched error strings (e.g.,"already merged"matched in 2 callsites,"INSUFFICIENT_SCOPES"in 2 callsites) should be promoted to sentinels.golang.org/x/sync/errgroupis completely absent; 4go func()sites inmcp_inspect_inspector.goandbootstrap_profile_runner.gomanage their own channels/panics manually.errorfwrapvlinter only catches%v-on-error; a complementary linter could flagfmt.Errorf("...", someErr)wheresomeErris passed as a non-%wargument without a format verb.Detailed Analysis
Top non-wrapping files:
pkg/workflow/engine_validation.go— 26 calls; all are leaf validation errors with no upstream cause. Correctly non-wrapping.pkg/cli/bootstrap_profile_manifest.go— 22 calls; manifest parse validation, no upstream cause. Correctly non-wrapping.pkg/workflow/stop_after.go— 16 calls; similarly leaf validation.pkg/workflow/time_delta.go— 14 calls; leaf validation.pkg/workflow/model_identifier.go— 14 calls; leaf validation.pkg/parser/schedule_parser.go— 13 calls; mixed: some lines call into a sub-parser that returns errors that are then wrapped in a freshfmt.Errorflosing chain.pkg/cli/add_package_manifest.go— 13 calls; some receiveyaml.Unmarshalerrors that are discarded.Production
strings.Contains(err.Error(), ...)suppressions:Of these,
"already merged"/"MERGED"and"INSUFFICIENT_SCOPES"are stable gh CLI error strings that recur in multiple files — ideal sentinel candidates.🤖 Tasks for Copilot Agent
NOTE TO PLANNER AGENT: Split the following tasks into individual work items.
Improvement Tasks
Task 1: Introduce Sentinel Errors for Recurring String Matches
Priority: High
Estimated Effort: Small
Focus Area: Error Chain Transparency
Description: Promote the two most-repeated error-string patterns —
"already merged"/"MERGED"(gh CLI merge status) and"INSUFFICIENT_SCOPES"(gh CLI auth scope) — to typed sentinel errors in a sharedpkg/errorutil(or add to the existingerrorutilpackage). Replace thestrings.Contains(err.Error(), ...)callsites inpkg/cli/add_interactive_git.goandpkg/cli/project_command.gowitherrors.Ischecks, removing the//nolint:errstringmatchsuppressions.Acceptance Criteria:
var ErrAlreadyMerged = errors.New("already merged")(or equivalent) defined inpkg/errorutil(orpkg/cli/git_errors.go).var ErrInsufficientScopes = errors.New("INSUFFICIENT_SCOPES")defined similarly.pkg/cli/add_interactive_git.go:22rewritten toerrors.Is(err, ErrAlreadyMerged)withoutnolint:errstringmatch.pkg/cli/project_command.go:322rewritten toerrors.Is(err, ErrInsufficientScopes)withoutnolint:errstringmatch.make test).Code Region:
pkg/cli/add_interactive_git.go,pkg/cli/project_command.go,pkg/errorutil/Introduce a sentinel
var ErrAlreadyMergedinpkg/errorutil(or a newpkg/cli/git_errors.gofile) and replace thestrings.Containscheck with anerrors.Isguard. Remove the//nolint:errstringmatchsuppression.In
pkg/cli/project_command.go(line 322), the code does:Introduce a sentinel
var ErrInsufficientScopesand replace witherrors.Is. Remove the//nolint:errstringmatchsuppression.Run
make fmt && make testto confirm no regressions.Task 3: Adopt errgroup for Structured Concurrency in mcp_inspect_inspector.go
Priority: Medium
Estimated Effort: Medium
Focus Area: Concurrency Safety
Description:
pkg/cli/mcp_inspect_inspector.golaunches 2 goroutines (lines 146 and 213) with manual channel management and inlinerecover()calls.golang.org/x/sync/errgroupis already an indirect dependency of the module. Replacing the ad-hoc pattern witherrgroup.WithContextwould unify error propagation, remove the manual panic recovery, and make cancellation explicit.Acceptance Criteria:
pkg/cli/mcp_inspect_inspector.gouseserrgroup.WithContext(fromgolang.org/x/sync/errgroup) instead of rawgo func()with manual channels.Gocall.Gocall.make fmt && make testpasses.Code Region:
pkg/cli/mcp_inspect_inspector.goTask 4: Linter — Flag fmt.Errorf With Error Argument But No %w Verb
Priority: Medium
Estimated Effort: Medium
Focus Area: Linter Coverage
Description: The existing
errorfwrapvlinter catchesfmt.Errorf("%v", err)(wrong verb). There is no linter for the complementary pattern:fmt.Errorf("message: "+err.Error())orfmt.Errorf("...", someErr)wheresomeErris passed as a regularinterface{}argument without using%w. Add a new analyzererrorfmterrconcatcheck(or extenderrorfwrapv) that flagsfmt.Errorfformat strings where anerror-typed argument is supplied as a positional arg but not via%w.Acceptance Criteria:
pkg/linters/detectsfmt.Errorf("foo %s", err)whereerrimplementserrorbut the corresponding verb is not%w.pkg/linters/linters.goandpkg/linters/doc.go.spec_test.goentry covers the new analyzer.make lint && make testpasses.Code Region:
pkg/linters/errorfwrapv/,pkg/linters/linters.go,pkg/linters/doc.goTask 5: Promote errstringmatch Suppressions to Typed Matches in schedule_preprocessing.go and update_extension_check.go
Priority: Low
Estimated Effort: Small
Focus Area: Error Chain Transparency
Description: Two remaining
strings.Contains(err.Error(), ...)production callsites —pkg/workflow/schedule_preprocessing.go:194("syntax is not supported") andpkg/cli/update_extension_check.go:438(dynamicmsgvariable) — suppresserrstringmatchwith no rationale comment. Add inline rationale comments explaining why typed matching is not possible (e.g., third-party library error without a typed sentinel) or replace with typed matches if feasible.Acceptance Criteria:
pkg/workflow/schedule_preprocessing.go:194has either a// errstringmatch: third-party cron library returns plain string errorscomment or is refactored to useerrors.Is.pkg/cli/update_extension_check.go:438has a rationale comment or is refactored.make lintpasses with no new suppressions.Code Region:
pkg/workflow/schedule_preprocessing.go,pkg/cli/update_extension_check.go📊 Historical Context
Previous Focus Areas
var _ Interfaceguards across 21 interfacesvar logshadows🎯 Recommendations
Immediate Actions (This Week)
nolintsuppressionsShort-term Actions (This Month)
Long-term Actions (This Quarter)
📈 Success Metrics
fmt.Errorfratio: 35% → <25% (eliminate accidental discard in parser/CLI paths)nolint:errstringmatchsuppressions: 4 → ≤2 (with rationale comments)errgroupadoption: 0 → ≥2 goroutine sitesvar ErrXxxvars: 4 → ≥6Next Steps
References:
Generated by Repository Quality Improvement Agent
Next analysis: 2026-07-18 — Focus area selected by diversity algorithm
Beta Was this translation helpful? Give feedback.
All reactions