gitproto: match CommandStatusErr by value so target-ref moves classify#73
Open
nodo wants to merge 1 commit into
Open
gitproto: match CommandStatusErr by value so target-ref moves classify#73nodo wants to merge 1 commit into
nodo wants to merge 1 commit into
Conversation
…bustly
asRefRejectedError and annotateLeaseFailure used errors.As against a
*packp.CommandStatusErr (pointer) target, but go-git returns CommandStatusErr
BY VALUE from ReportStatus.Error() (value receiver, constructed by value in
report_status.go). A pointer target never matches a value in the error chain,
so on the real receive-pack path the errors.As fell through, no *RefRejectedError
was built, moved was never set, and errors.Is(err, ErrTargetRefMoved) was always
false. Every live concurrent target-ref rejection ("remote ref has changed")
passed through unclassified — the typed-rejection feature was inert in
production. The same bug silently disabled the lease-failure hint.
Extract the error through a shared commandStatusErr helper that accepts BOTH the
value form (today's go-git) and a *CommandStatusErr, since errors.As is exact
about value-vs-pointer and the form is not part of go-git's (alpha) API contract
— so a future switch can't silently regress classification to "every rejection
unclassified".
The existing unit tests masked the original bug by feeding a pointer
&packp.CommandStatusErr{} as input, which the pointer target did match — not how
go-git hands the error over. Make the table tests build input via go-git's real
(&CommandStatus{}).Error() (value), add TestAsRefRejectedError_RealReportStatusPath
(drives a real ReportStatus.Error() end to end; fails loud on a deeper type
change) and TestAsRefRejectedError_ToleratesPointerCommandStatusErr (pins the
pointer form).
Bug was in the unreleased typed-rejection feature (#71); never shipped tagged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f226bb2085bf
7d7406e to
06be847
Compare
Soph
approved these changes
Jun 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
ErrTargetRefMoved(PR #71, unreleased) never attaches on the real push path.asRefRejectedErrorandannotateLeaseFailurematch go-git'sCommandStatusErrwith a pointer target:but go-git returns
CommandStatusErrby value fromReportStatus.Error()(value receiverError(), constructed by value inreport_status.go).errors.Asagainst a**CommandStatusErrtarget never matches a value-typedCommandStatusErrin the chain, so:asRefRejectedErrorfalls through → no*RefRejectedError,movednever set →errors.Is(err, ErrTargetRefMoved)is alwaysfalseon a live receive-pack rejection.annotateLeaseFailurefalls through → the lease-failure hint never fires.Net effect: every concurrent target-ref rejection (
remote ref has changed) passes through unclassified. The downstream mirror-pipeline worker that relies onerrors.Is(err, ErrTargetRefMoved)to classify benign races sees nothing — the typed-rejection feature is inert in production.The existing tests masked this by feeding
&packp.CommandStatusErr{}(a pointer) as input, which the pointer target does match — but that is not how go-git hands the error over.Fix
errors.Astarget (var cs packp.CommandStatusErr), matching go-git's actual return type. go-git's own tests confirm a value target is correct (s.ErrorAs(rs.Error(), &CommandStatusErr{})).(&packp.CommandStatus{...}).Error()(value), and the underlying-error assertions use value targets.TestAsRefRejectedError_RealReportStatusPathdrives a realReportStatus.Error()end to end through the production wrap — it fails on the pointer target and passes on the value target, so a pointer-vs-value relapse fails CI.Verification
go test ./...,go vet,gofmtall clean. Confirmed the new test fails on the pre-fix pointer target and passes after.Bug was in the unreleased typed-rejection feature (#71); never shipped in a tagged release.
🤖 Generated with Claude Code
Note
Medium Risk
Changes error-classification on the push path (retry vs fatal behavior for embedders using
ErrTargetRefMoved), but the fix restores intended unreleased behavior rather than altering semantics.Overview
Fixes a bug where typed push rejections never activated on real receive-pack failures.
annotateLeaseFailureandasRefRejectedErrorused a pointererrors.Astarget for go-git'spackp.CommandStatusErr, but go-git returns that error by value fromReportStatus.Error()— so livengstatuses passed through unwrapped,errors.Asto*RefRejectedErrorfailed, anderrors.Is(err, ErrTargetRefMoved)stayed false for concurrent CAS/lease rejections likeremote ref has changed.Both helpers now use a value
errors.Astarget so lease hints andRefRejectedError/ErrTargetRefMovedclassification work on the production path. Tests build inputs viaCommandStatus.Error()(not hand-made pointers), and a new regression test drives a realReportStatus.Error()through the wrap chain so a pointer-target relapse fails CI. CHANGELOG documents the fix and notes callers should use a valueerrors.Asfor the underlyingCommandStatusErr.Reviewed by Cursor Bugbot for commit 7d7406e. Configure here.