[sergo] Sergo Report: error-wrapping-plus-context-propagation - 2026-02-24 #18227
Closed
Replies: 2 comments
-
|
/plan |
Beta Was this translation helpful? Give feedback.
0 replies
-
|
This discussion was automatically closed because it expired on 2026-02-25T22:25:34.294Z.
|
Beta Was this translation helpful? Give feedback.
0 replies
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.
-
Date: 2026-02-24
Strategy:
error-wrapping-plus-context-propagationSuccess Score: 8/10
Run ID: §22372431560
Executive Summary
Today's Sergo run combined a proven error-patterns strategy with a novel
sync.Oncereset audit. The cached component searched forfmt.Errorfcalls using%s/%vto format error values instead of the%wverb — the pattern that breaks Go'serrors.Is/errors.Aschain inspection. Most uses proved legitimate (formatting non-error strings), but one real error chain breakage was found in the workflow validation path.The new exploration component uncovered a more significant finding: two cache-clearing functions (
ClearCurrentRepoSlugCacheandClearRepositoryFeaturesCache) resetsync.Oncevalues via direct struct assignment (= sync.Once{}), which violates the Go memory model and constitutes a data race if any concurrent goroutine is still inside or has returned fromDo(). A third finding —checkActorPermissioncreating its owncontext.Background()-derived context while its callers already hold a live MCP request context — rounds out the run.Three actionable improvement tasks are generated below, covering a data race fix, a context propagation improvement, and an error chain repair.
Serena Tools Update
Tools Used Today
activate_projectsearch_for_patternfmt.Errorfwith%s/%v+ err,sync.Once{}assignments,context.Background()usagesfind_referencing_symbolscheckActorPermission,ClearCurrentRepoSlugCache,ClearRepositoryFeaturesCacheget_symbols_overviewmcp_server_helpers.goStrategy Selection
Cached Reuse Component (50%)
Previous Strategy Adapted:
symbol-analysis-plus-error-patterns(Run 1, 2026-02-21, score 8/10)fmt.Errorfwith%s/%vformatting anerrvariable specifically — the subset of usages that silently breakerrors.Is/errors.Aschains. Previous run checked broader patterns; today's search was more precise.pkg/cli/andpkg/workflow/non-test Go files.New Exploration Component (50%)
Novel Approach:
sync.Oncereset audit + context propagation gap analysissync.Oncecaches that need to be "reset" (e.g., for testing) are often reset by direct struct assignment — a pattern Go'ssyncpackage explicitly prohibits after first use.search_for_patternforsync\.Once\{\}andcontext\.Background\(\)with surrounding context lines;find_referencing_symbolsto understand call sites.sync.Onceglobals with associated clear/reset functions; MCP server handler chain for context propagation.Combined Strategy Rationale
Error wrapping and
sync.Oncepatterns both relate to correctness in concurrent Go code — one silently hides failure modes from callers, the other introduces undefined behavior under concurrent access. Together they provide both depth (specific error chain issue) and breadth (systemic data race pattern across two packages).Analysis Execution
Codebase Context
pkg/cli,pkg/workflowFindings Summary
Detailed Findings
High Priority Issues
H1 —
sync.Oncereset via direct struct assignment (data race)Two cache-clear functions reset
sync.Onceby writing= sync.Once{}, which is a data race per the Go memory model if any goroutine is concurrently insideDo()or reading the once's internal state.pkg/cli/repo.go:26getCurrentRepoSlugOnce = sync.Once{}pkg/workflow/repository_features_validation.go:88getCurrentRepositoryOnce = sync.Once{}Both functions also unsynchronised-write their companion result/error variables alongside the
sync.Oncereset — a second data race avenue ifGetCurrentRepoSlug()is running concurrently.The Go
syncpackage documentation states: "A Once must not be copied after first use." Direct struct assignment is semantically equivalent to a copy and clobbers the internaldone uint32atomic counter and embeddedsync.Mutexwithout going through any synchronization primitive.Currently both callers are test functions (sequential in practice), so no crash has been observed — but:
t.Parallel()that calls the clear function creates a real race.-race) will flag this when triggered concurrently.Medium Priority Issues
M1 —
checkActorPermissionignores available MCP request contextcheckActorPermissioninpkg/cli/mcp_server_helpers.gocreates its own independent context:But both of its callers —
registerLogsToolandregisterAuditToolinmcp_tools_privileged.go— are MCP handler closures that already receive a live requestctx:If the MCP client disconnects or the server shuts down, the parent
ctxwill be cancelled — butcheckActorPermissionwill continue the GitHub API call for up to 5 seconds ignoring that cancellation signal.M2 — Error chain broken after
errors.Astype assertion inrun_workflow_validation.goThe
ExitErrorbranch uses%swithexitError.Stderr(a[]byte), discardingerrentirely. Callers cannot useerrors.Is(err, someErr)orerrors.As(err, &exec.ExitError{})on the returned error. The non-ExitError branch immediately below correctly uses%w. The two branches are inconsistent and theExitErrorcase — the more specific and informative one — is the one that loses the chain.Low Priority / Observations
pkg/workflow/compiler_orchestrator_frontmatter.go:41uses%vintentionally with//nolint:errorlintcomment explaining the rationale (avoiding exposure ofos.PathErrorinternal path details). This is a well-documented exception, not an issue.pkg/workflow/engine_validation.go:96andpkg/workflow/github_tool_to_toolset.go:119usefmt.Errorf("%s", errMsg)whereerrMsgis astrings.Builder— not an error value, so%wdoes not apply.pkg/cli/actionlint.goandpkg/cli/mcp_inspect_mcp.gousecontext.Background()in functions that do not accept a context parameter — these are entry points, not propagation gaps.Improvement Tasks Generated
Task 1: Replace
sync.Oncereset-by-assignment with a thread-safe resettable cache patternIssue Type: Concurrency / Go Memory Model
Problem:
ClearCurrentRepoSlugCache()inpkg/cli/repo.goandClearRepositoryFeaturesCache()inpkg/workflow/repository_features_validation.goresetsync.Oncevalues by direct struct assignment (= sync.Once{}). This is a data race under concurrent access and violates the Gosyncpackage contract.Locations:
pkg/cli/repo.go:17-29—getCurrentRepoSlugOnce+ClearCurrentRepoSlugCachepkg/workflow/repository_features_validation.go:64-93—getCurrentRepositoryOnce+ClearRepositoryFeaturesCacheImpact:
-racedetector; undefined behavior if clear is called concurrently with cache readRecommendation: Replace the
sync.Once+ companion variables pattern with async.RWMutex-guarded struct oratomic.Pointerholding a nullable result. A minimal fix:Validation:
go test -race ./pkg/cli/... ./pkg/workflow/...— should report no data racesTestClearCurrentRepoSlugCacheandTestGetCurrentRepoSlugstill passgetCurrentRepositoryOnceinrepository_features_validation.goEstimated Effort: Small–Medium
Task 2: Thread
ctx context.ContextthroughcheckActorPermissionIssue Type: Context Propagation
Problem:
checkActorPermissioninpkg/cli/mcp_server_helpers.gocreates a freshcontext.WithTimeout(context.Background(), 5*time.Second)instead of deriving from the caller's MCP request context. Both call sites have a livectx context.Contextavailable from the MCP handler closure.Locations:
pkg/cli/mcp_server_helpers.go:169— function signature (missingctxparam)pkg/cli/mcp_server_helpers.go:205—context.Background()usagepkg/cli/mcp_tools_privileged.go:73-74— caller ignoringctxpkg/cli/mcp_tools_privileged.go:264-265— second caller ignoringctxImpact:
Recommendation:
Validation:
mcp_tools_privileged.gogo build ./pkg/cli/...Estimated Effort: Small
Task 3: Restore error chain in
run_workflow_validation.goExitError branchIssue Type: Error Wrapping
Problem: When
RunGHreturns an*exec.ExitError, the error branch atpkg/cli/run_workflow_validation.go:288discards the original error and formats only the stderr bytes using%s. The immediately following branch correctly uses%w. This inconsistency breakserrors.Is/errors.Asfor the most-common failure case (non-zero exit fromgh workflow list).Location:
pkg/cli/run_workflow_validation.go:286-290Impact:
ghexit errors from other errors; structured error handling (retry logic, user-facing error classification) is silently defeated for theExitErrorpathRecommendation:
Validation:
go vet ./pkg/cli/...— passesvalidateWorkflowExistscan now useerrors.As(err, &exec.ExitError{})successfullygo test ./pkg/cli/...Estimated Effort: Small
Success Metrics
This Run
pkg/cliandpkg/workflowScore Reasoning
sync.Oncerace is a genuine Go memory model violation; both medium findings are concrete and actionable. Minus one point because all three callers ofClearCurrentRepoSlugCacheare test-only, reducing immediate production risk.pkg/parseror utility packages this run.Historical Context
Strategy Performance (all runs)
Cumulative Statistics
error-patterns-plus-field-registry-audit(9/10)Recommendations
Immediate Actions
sync.Oncereset data race (High) — Replace bothClearCurrentRepoSlugCacheandClearRepositoryFeaturesCachewithsync.RWMutex-guarded resettable cache structs. Run with-raceto validate.checkActorPermission(Medium) — Small, safe change: addctx context.Contextparameter and derive the 5-second timeout from it. Two call sites to update.%swith%wand includeerras wrap target, preserving stderr bytes in the format string.Long-term Improvements
ScriptRegistry-style pattern for resettable caches: Thepkg/workflow/script_registry.goalready documents a centralized registry pattern to eliminate scatteredsync.Onceglobals. A similarCacheRegistrypattern would give a single place to reset all caches (e.g., in tests) without touchingsync.Onceinternals.go test -raceto CI forpkg/cliandpkg/workflow: Thesync.Onceassignment race would be caught automatically. This is the idiomatic Go safety net for concurrent code patterns.Next Run Preview
Suggested Focus Areas
pkg/parser/— the largest file (import_processor.go, 1106 LOC) andmcp.go(796 LOC) have not been analyzed for symbol quality, type patterns, or error handlingpkg/workflow/cache.go(871 LOC) — large cache file likely has mutex/sync patterns worth auditinggo func()calls without associatedcontext.Done()selectsStrategy Evolution
error-patterns-plus-field-registry-auditstrategy scored highest (9/10) — another registry-focused audit of a different subsystem (e.g., parser hooks or workflow job registry) could be effectiveReferences:
Beta Was this translation helpful? Give feedback.
All reactions