Repository navigation
Grouped go_modules updates drop deletions of vendored files (regression from #16290) #16527
Description
Is there an existing issue for this?
- I have searched the existing issues
Package ecosystem
go_modules
Package manager version
Go (as bundled in the Dependabot updater image, run of 2026-10-01)
Language version
go 1.26.0 (go.mod)
Manifest location and content before the Dependabot update
/go.mod with a committed vendor/ directory: https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/blob/d340dba7bbd8e3319b16c80e822ecaa7901d2c7c/go.mod
dependabot.yml content
version: 2
updates:
- package-ecosystem: gomod
directory: /
open-pull-requests-limit: 10
schedule:
interval: monthly
groups:
security:
applies-to: security-updates
patterns:
- "*"
k8s:
patterns:
- k8s.io/api
- k8s.io/apimachinery
- k8s.io/client-go
- k8s.io/component-base
- k8s.io/kubelet
- k8s.io/mount-utilsUpdated dependency
k8s group: k8s.io/apimachinery, k8s.io/client-go, k8s.io/component-base, k8s.io/kubelet, k8s.io/mount-utils from 0.36.2 to 0.37.1 (which also bumps go-openapi/swag 0.23 → 0.27, kube-openapi, etc.).
What you expected to see, versus what you actually saw
Expected: the PR commit matches go get … && go mod tidy && go mod vendor, including deletions of vendored files that are no longer needed.
Actual: grouped PR kubernetes-sigs/alibaba-cloud-csi-driver#1838 adds and modifies vendored files but deletes none. 83 stale files remain in vendor/, so the build breaks:
vendor/github.com/go-openapi/swag/convert.go:31:6: IsFloat64AJSONInteger redeclared in this block
vendor/k8s.io/api/extensions/v1beta1/zz_generated.validations.go:128:33: undefined: validate.EachSliceVal
Running go mod tidy && go mod vendor on the PR branch produces only those 83 deletions (go.mod, go.sum, vendor/modules.txt are unchanged), so the version resolution is fine; only the deletions are lost.
This is a regression. The PR was regenerated several times, and the older head commits are still fetchable:
| head commit | date | added | modified | deleted | renamed |
|---|---|---|---|---|---|
b482d47e9 |
2026-09-03 | 294 | 958 | 45 | 38 |
bbe238c4f |
2026-09-18 | 332 | 958 | 0 | 0 |
11e9b00c4 |
2026-09-21 | 332 | 922 | 0 | 0 |
2c15fbb80 |
2026-10-01 | 332 | 922 | 0 | 0 |
Earlier grouped PRs in the same repo also contained vendored deletions (e.g. kubernetes-sigs/alibaba-cloud-csi-driver#1465, 24 removed files).
Suspected cause
The job source uses directories ("source":{…,"directories":["/"]} in the job definition), so since #16290 (merged 2026-09-16) both create and refresh go through compile_all_dependency_changes_for_directories, which passes every changed file through apply_file_change:
dependabot-core/updater/lib/dependabot/updater/group_update_creation.rb
Lines 236 to 247 in 4725d25
| def apply_file_change(file, working_files_by_path, changed_files_by_path, initial_paths, created_paths) | |
| if file.deleted? | |
| working_files_by_path.delete(file.path) | |
| created_paths.delete(file.path) | |
| if initial_paths.include?(file.path) | |
| changed_files_by_path[file.path] = | |
| file | |
| else | |
| changed_files_by_path.delete(file.path) | |
| end | |
| return | |
| end |
initial_paths is built from dependency_snapshot.all_dependency_files (L192-L197), i.e. only the fetched manifests (go.mod, go.sum). Vendored files are never in it, so every vendored DELETE is treated as "created during this group, then deleted" and silently dropped. Vendored files that are modified are similarly re-labelled CREATE instead of UPDATE, which happens to be harmless because their content is sent in full.
Up to that point the deletions are tracked correctly: VendorUpdater reports them from git status and DependencyGroupChangeBatch keeps them as DELETE. #16314 addressed the same "vendored paths aren't in the initial set" gap inside DependencyGroupChangeBatch, but apply_file_change still relies on initial_paths.
Possible fix: for files not in initial_paths, decide based on whether the path exists in the base commit (or keep the operation computed by DependencyGroupChangeBatch, which already tracks initially_exists per path), instead of assuming they were created during the group.
This likely affects any grouped update with vendored files (go_modules vendor/, bundler vendor/cache, …) whose job source uses directories. Note our config only sets directory: /, yet the job arrives with directories: ["/"].
Native package manager behavior
go get k8s.io/apimachinery@v0.37.1 k8s.io/client-go@v0.37.1 k8s.io/component-base@v0.37.1 k8s.io/kubelet@v0.37.1 k8s.io/mount-utils@v0.37.1 && go mod tidy && go mod vendor yields the same go.mod/go.sum/vendor/ as the Dependabot PR plus 83 deleted files under vendor/ (e.g. vendor/github.com/go-openapi/swag/{convert,json,util,...}.go, vendor/github.com/mailru/easyjson/**, vendor/github.com/josharian/intern/**, vendor/k8s.io/api/apidiscovery/v2beta1/**, vendor/k8s.io/api/scheduling/v1alpha2/**, vendor/k8s.io/api/extensions/v1beta1/zz_generated.validations.go). Manually fixed version: kubernetes-sigs/alibaba-cloud-csi-driver#1868
Images of the diff or a link to the PR, issue, or logs
- Broken PR: build(deps): bump the k8s group across 1 directory with 5 updates kubernetes-sigs/alibaba-cloud-csi-driver#1838
- Dependabot job log (job 1601987902,
recreate): https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/actions/runs/36827115865 - Last good version of the PR commit (with deletions): kubernetes-sigs/alibaba-cloud-csi-driver@b482d47
- First bad version: kubernetes-sigs/alibaba-cloud-csi-driver@bbe238c
Smallest manifest that reproduces the issue
Any Go module with a committed vendor/ directory, a grouped update configured via directories (or directory, if it is normalized to directories), where one of the bumps makes go mod vendor remove a previously vendored file.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
huww98 commentedon Oct 10, 2026
I confirmed this locally with the dependabot CLI and the current
dependabot-updater-gomodimage (itsupdater/libandcommon/libare identical tomainat 4725d25), by mounting agit revertof #16290 overupdater/libfor the B side.Minimal reproduction
https://github.com/huww98/dependabot-vendor-delete-repro (runs in ~1 minute): a module vendoring
github.com/cespare/xxhash/v2v2.1.2, a singleallgroup, and ajob.ymlwithdirectories: [/]. Upgrading to v2.3.0 makesgo mod vendordeletevendor/github.com/cespare/xxhash/v2/xxhash_amd64.go.updated-dependency-filesin thecreate_pull_requestpayload:mainmainwith #16290 revertedmain, job usesdirectory: /delete xxhash_amd64.goxxhash.go,modules.txt, …)createupdateupdatexxhash_asm.go, …)createcreatecreateThe lost deletion leaves a stale file that breaks the build:
Original job
Replaying the job definition from the original run (pinned to the same base commit) gives the same result at scale. The vendored file operations:
mainmainwith #16290 revertedgo get … && go mod tidy && go mod vendorWith the revert, the delete/create/update file lists and contents match the manual result exactly.
Side note for anyone replaying a real job definition with the CLI: GitHub enables the
blocked-versionsexperiment, and dependabot CLI v1.94.0 doesn't serve theGET …/blocked_versionsendpoint, so the job aborts withMalformed blocked versions responseunless the CLI's fake API is patched to answer{"data":[]}. The minimal repro doesn't enable it and works with the stock CLI.