Skip to content

Go apply and vendor break every build in projects with a committed vendor/ directory (modules.txt not synced), while apply --check and VEX report success #343

Description

[agent] Found by the scheduled Go modules bug-hunt routine (ledger #317).

Summary

In a Go project with a committed vendor/ directory (go mod vendor), both agent-mode apply and vendored-mode vendor add a go.mod replace and exit 0. Neither touches vendor/modules.txt. From then on, every go build / go run / go test fails:

go: inconsistent vendoring in …/c:
	example.com/upstream@v1.0.0: is replaced in go.mod, but not marked as replaced in vendor/modules.txt

With a vendor/ directory and go ≥ 1.14 in go.mod, go defaults to -mod=vendor and checks that modules.txt records every go.mod replace. apply --check still says Patch redirects are in sync (exit 0). vex attests not_affected. Nothing tells the user to re-run go mod vendor. rollback then breaks the build a second time, the other way round ("is marked as replaced in vendor/modules.txt, but not replaced in go.mod").

Impact

Committing vendor/ is common in large Go repos (Kubernetes-style monorepos, air-gapped CI). There, a successful socket-patch apply or vendor leaves the default build broken on every machine and in CI. That's "a rewrite that makes the next frozen install fail", and the CLI's own audit (apply --check) and VEX both call the state healthy. Running go mod vendor afterwards does produce a correct patched build (it copies the patched tree into vendor/), so the fix is to either do that sync (or its modules.txt equivalent) or refuse or warn up front.

Repro (hermetic file GOPROXY, the same fixture shape as tests/e2e_golang_build.rs)

export GOTOOLCHAIN=local GOSUMDB=off GOENV=off GOFLAGS= GOPROXY=file://$T/proxy GOMODCACHE=$T/modcache
# consumer requires example.com/upstream v1.0.0; go.mod says `go 1.16`
go mod vendor && go build ./...                    # ok
socket-patch apply --offline --ecosystems golang   # "1 of 1 targeted patch applied", exit 0
go build ./...                                      # exit 1: inconsistent vendoring (above)
socket-patch apply --check --ecosystems golang     # "Patch redirects are in sync (1 redirect checked)", exit 0
socket-patch vex --offline --output vex.json       # exit 0, not_affected
go mod vendor && go run .                          # OUT: PATCHED (the manual sync fixes it)
socket-patch rollback --offline --ecosystems golang  # exit 0
go build ./...                                      # exit 1: "is marked as replaced in vendor/modules.txt, but not replaced in go.mod"

socket-patch vendor --offline --ecosystems golang in place of apply behaves the same: exit 0, the replace => ./.socket/vendor/golang/<uuid>/… is written, the build fails with the same error, and vex attests not_affected.

Expected vs actual

  • Expected: docs/ecosystems.md ("Go: directory replaces and go.sum") describes the replace as the complete mechanism ("the committed patched tree itself is the protection … the wiring survives go mod tidy"). docs/design/golang-hosted.md says "go mod vendor vendors the PATCHED bytes … the vendored escape hatch composes". So a project that uses vendor/ should keep building after apply / vendor: either vendor/modules.txt (and vendor/<module>) get synced, or the command refuses or at least warns (go mod vendor required) and apply --check reports the inconsistency.
  • Actual: exit 0, a broken build, apply --check in sync, and VEX attests. vendor/modules.txt is never read or written anywhere in crates/ (README: "vendor/modules.txt is not read").

Matrix (probe run https://github.com/SocketDev/socket-patch/actions/runs/36746894687, plus local)

OS go agent apply vendored vendor
Linux 1.16.15 fail (build broken, exit 0) fail
Linux 1.21.13 fail fail
Linux 1.24.7 (local, 5 repros) fail fail
Linux 1.26.3 fail fail
macOS 1.24.13 / 1.26.3 fail fail
Windows 1.16.15 / 1.21.13 / 1.26.3 not reached: Go apply/vendor already fail on Windows with Access is denied. (os error 5), to be filed separately not reached

Release 4.0.0 behaves the same (checked locally). I couldn't compare 3.3.0 because it doesn't accept the hand-staged manifest. A go 1.13 go.mod with an explicit -mod=vendor fails the same way on go 1.24.

Suspect code

  • crates/socket-patch-core/src/patch/redirect/golang_local.rs:170 apply_go_redirect → go_mod_edit::ensure_replace_entry (crates/socket-patch-core/src/vendor/go_mod_edit.rs:175): only go.mod is edited.
  • crates/socket-patch-core/src/vendor/golang.rs:209 vendor_go_module: same.
  • golang_local::verify_go_redirect_state (apply --check) doesn't look at vendor/modules.txt.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedbugSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentpm:goGo modulespriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions