Date: April 2026
Branch: master (commit 5a555755ba2)
Scope: hack/unwanted-dependencies.json — modules listed in spec.unwantedModules that are still present in vendor/
Kubernetes maintains a blocklist of dependencies that should not appear in the vendor tree, defined in hack/unwanted-dependencies.json. The file has two sections:
spec.unwantedModules— the full blocklist with rationale for each entrystatus.unwantedVendored— modules from the blocklist that are currently vendored (updated when the lint check runs)status.unwantedReferences— which of our direct dependencies are responsible for pulling each blocked module in transitively
As of April 2026, the blocklist contains ~80 modules. The vast majority have already been removed from vendor. 10 remain, all pulled in transitively by upstream dependencies that have not yet removed them. This report documents the current state of each, the upstream blockers, and the known issues/PRs in those repositories.
Identified by exact match against vendor/modules.txt:
| Module | Reason in blocklist |
|---|---|
github.com/davecgh/go-spew |
refer to #103942 |
github.com/gogo/protobuf |
unmaintained |
github.com/golang/protobuf |
replace with google.golang.org/protobuf |
github.com/google/btree |
unmaintained, archive mode |
github.com/json-iterator/go |
refer to #105030 |
github.com/mailru/easyjson |
unmaintained |
github.com/modern-go/concurrent |
problematic reliance on golang internals |
github.com/modern-go/reflect2 |
problematic reliance on golang internals |
golang.org/x/exp |
experimental/deprecated subrepository |
gopkg.in/yaml.v3 |
prefer sigs.k8s.io/yaml/goyaml.v3 |
Note:
github.com/modern-go/concurrentandgithub.com/modern-go/reflect2are transitive dependencies ofgithub.com/json-iterator/go. Their fate is fully coupled to json-iterator's removal.
Rationale: Archived/unmaintained upstream.
Kubernetes vendor version: (via go.etcd.io/etcd/server/v3 v3.6.8)
Primary upstream blocker: go.etcd.io/etcd/server/v3
Upstream status:
- etcd issue #20991 ("Evaluate and replace deprecated
github.com/google/btree") — closed/completed. The last comment reads "Thanks! Closing this ticket..." confirming the work landed. - On etcd
main,github.com/google/btreehas been fully replaced with a fork embedded ink8s.io/utils/third_party/forked/golang/btree. The change is inserver/storage/mvcc/index.goand related files. - etcd v3.6.8 (vendored by Kubernetes): still depends on
google/btree v1.1.3 - etcd v3.6.10 (latest release, 2026-04-01): still depends on
google/btree v1.1.3
Prognosis: ✅ Done upstream — waiting on the next etcd release after v3.6.10 to cut a release from a commit that includes the main-branch removal.
Rationale: See kubernetes/kubernetes#103942. Unmaintained; Go's fmt package (%#v, sorted map output) now covers most use cases.
k8s.io/utils/dump imports go-spew and exposes a ForHash function. The file contains an explicit comment: "The config MUST NOT be changed because that could change the result of a hash operation." This makes removal harder than a simple dependency bump — it requires either a byte-for-byte compatible replacement or a checkpoint/label migration on running clusters.
Production call sites in k/k (non-test):
pkg/util/hash/hash.go—DeepHashObjectcallsdump.ForHash; used for pod template hashes, controller revision hashes, endpoint slice hashespkg/kubelet/cm/cpumanager/state/checkpoint.go—MarshalCheckpointandVerifyChecksumcalldump.ForHashdirectly; the hash is stored in and validated from kubelet checkpoint files on diskpkg/kubelet/cm/memorymanager/state/checkpoint.go— same patternpkg/controller/controller_utils.go,pkg/controller/history/controller_history.go,pkg/controller/job/job_scheduling_manager.go,pkg/kubelet/config/common.go,pkg/kubelet/container/helpers.go,pkg/api/v1/endpoints/util.go,staging/.../endpointslice/util/controller_utils.go
Upstream transitive blockers:
stretchr/testify, prometheus/common, go-openapi/jsonpointer, go-openapi/swag, go-logr/zapr, sirupsen/logrus, go.uber.org/{goleak,multierr,zap}, grpc-ecosystem/go-grpc-middleware/v2, github.com/google/cadvisor, go.etcd.io/etcd/*, all go.opentelemetry.io/* packages, k8s.io/kube-openapi, k8s.io/utils, sigs.k8s.io/kustomize/*
Upstream status:
stretchr/testifyis the most impactful transitive blocker.- Issue testify#1061 ("Possible to remove go-spew?") — open since 2018. Long-running discussion with no resolution.
- testify
masterhas removedgo-spew— therequireblock no longer includes it. However, the last release was v1.11.1 (August 2025) and no new release has been cut since.
- All other transitive blockers — prometheus/common, go-logr/zapr, sirupsen/logrus, go.uber.org/, go-openapi/, cadvisor, etcd, otel packages — still carry
go-spewat their latest releases with no active removal efforts identified.
Prognosis: 🔴 Even if all transitive upstreams dropped go-spew, k/k itself would still need it through k8s.io/utils/dump.ForHash. Removal requires replacing ForHash with a compatible alternative and providing a migration path for kubelet checkpoint files and hashed labels already written on running nodes. This is a k/k-internal design problem, not just an upstream one.
Rationale: Prefer sigs.k8s.io/yaml/goyaml.v3 (the same upstream, re-exported under a stable import path).
Primary upstream blockers:
stretchr/testify, prometheus/common, go-openapi/jsonpointer, go-openapi/swag, go-logr/zapr, grpc-ecosystem/go-grpc-middleware/v2, go.uber.org/{goleak,multierr,zap}, github.com/google/cadvisor, go.etcd.io/etcd/*, all go.opentelemetry.io/* packages, k8s.io/kube-openapi, sigs.k8s.io/kustomize/*, github.com/containerd/containerd/api
Upstream status:
- PR testify#1772 ("change yaml library to
go.yaml.in/yaml/v3") — open. This would switch testify fromgopkg.in/yaml.v3to the new canonical import pathgo.yaml.in/yaml/v3(same code, maintained by the same author under a stable domain). - Note:
go-openapi/swagmaster already usesgo.yaml.in/yaml/v3(alongsidegopkg.in/yaml.v3transitively), and their newest jsonpointer v0.23.1 drops thegopkg.inpath entirely — but those versions require Go 1.25 (see §5 below). - Every other upstream still carries
gopkg.in/yaml.v3with no active removal PRs.
Prognosis: 🔴 Very widely embedded. Even removing it from testify (via PR#1772) would only reduce the count. The long tail across etcd, otel, containerd, cadvisor, kustomize makes this the hardest module to fully remove.
Rationale: Unmaintained. kube-openapi should use its own internal JSON machinery.
Primary upstream blockers:
k8s.io/kube-openapi → go-openapi/jsonpointer v0.21.0, go-openapi/swag v0.23.0; also sigs.k8s.io/kustomize/{api,kyaml}
Upstream status:
The go-openapi project has undergone a major restructuring:
go-openapi/swagrefactored into sub-modules starting around v0.25.x. The sub-modules (swag/jsonname,swag/jsonutils, etc.) do not depend onmailru/easyjson. The rootswagpackage re-exports the same symbols so no import-path changes are needed in consumers.go-openapi/jsonpointer v0.23.1(released 2026-04-18): completely rewritten, depends ongo-openapi/swag/jsonname v0.26.0instead ofmailru/easyjson. Go directive:go 1.25.0.go-openapi/swagmaster has also fully removedeasyjson, requiring Go 1.25.
The critical path for Kubernetes:
- PR kube-openapi#590 — open, filed by dims. Upgrades
go-openapi/swagtov0.25.4, which is the last version of the split-module approach that works with Go 1.24. The PR also bumps thegodirective to 1.24 and fixes two pre-existing vet errors exposed by the bump. Status: self-approved by dims, assigned toJefftreeandapelisse, needs LGTM fromsttts. - kustomize also carries easyjson transitively; a bump of kube-openapi in kustomize would likely pull it through.
Prognosis: 🟡 There is a concrete, mergeable PR in kube-openapi (#590). The main bottleneck is reviewer bandwidth.
Rationale: json-iterator (and its modern-go dependencies) relies on unstable Go runtime internals via reflect2. See kubernetes/kubernetes#105030. Every new Go release requires a patch to keep it working.
Primary upstream blockers:
prometheus/client_golang v1.23.2, k8s.io/kube-openapi (commit 43fb72c5454a), sigs.k8s.io/structured-merge-diff/v6 v6.3.2
Upstream status:
prometheus/client_golang: No removal effort. The project went in the opposite direction — PR #1225 (merged 2023) extended json-iterator usage. json-iterator is used for performance-sensitive scrape-result encoding.k8s.io/kube-openapi: PR #326 ("usek8s.io/apimachinery/pkg/util/jsoninstead of json-iterator") was closed without merge. Currently no open replacement PR. An older PR #246 ("Switch to stdlib json encoding") was merged, but kube-openapi still has json-iterator in its go.mod as of the March 2026 commit we vendor.sigs.k8s.io/structured-merge-diff: Issue #202 ("remove use of json-iterator / reflect2") — open and actively tracked. The latest comment explicitly states: "I don't expect us to merge this before stdlib ships json/v2 in a non-experimental fashion (Go 1.26 at the earliest)". The replacement under investigation is the newencoding/json/v2package (tracking at golang/go#71497).
Prognosis: 🔴 Gated on Go 1.26 stdlib encoding/json/v2. Go 1.25 is expected mid-2026 and does not include json/v2 as stable; Go 1.26 would be early 2027 at the earliest. This is the longest tail on the list.
Rationale: The github.com/golang/protobuf package is the deprecated v1 API for protobuf in Go. It should be replaced with google.golang.org/protobuf (the modern v2 API).
Primary upstream blockers:
google.golang.org/grpc v1.80.0 (primary), github.com/containerd/containerd/api v1.10.0, github.com/containerd/ttrpc v1.2.7, go.etcd.io/etcd/{api,client,server}/v3 v3.6.8, sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0, github.com/container-storage-interface/spec
Upstream status:
google.golang.org/grpc: Issue grpc-go#8940 ("depends on deprecatedgithub.com/golang/protobuf") was closed with an explicit maintainer response: "As long as we are on grpc major version 1 (and there is no plan of doing a new major version),github.com/golang/protobufis required for backward compatibility with applications using proto-generated code from older versions of the code generator."- grpc-go
masterstill carriesgithub.com/golang/protobuf v1.5.4. - The
golang/protobufpackage itself still exists as a compatibility shim overgoogle.golang.org/protobuf, andgoogle.golang.org/protobufitself listsgithub.com/golang/protobufin its own go.mod (they cross-reference for the shim). - konnectivity-client v0.34.0 has
github.com/gogo/protobuf v1.3.2 // indirect.
Prognosis: 🔴 Effectively a won't-fix for grpc-go v1.x. Would require grpc-go to cut a major version (v2), which has no announced timeline. This is structurally the hardest removal.
Rationale: Unmaintained. The project has not had a release since 2021 (v1.3.2). gRPC and protobuf work has consolidated around google.golang.org/protobuf.
Primary upstream blockers:
github.com/containerd/ttrpc v1.2.7, github.com/containerd/typeurl/v2 v2.2.3, github.com/containerd/containerd/api v1.10.0, github.com/google/cadvisor v0.56.2, go.etcd.io/etcd/server/v3 v3.6.8, sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0
Upstream status:
containerd/ttrpc: Issue ttrpc#97 ("New branch for removing gogo/protobuf") — open but stalled. The issue describes creating av2branch to hold gogo-free code, since removal requires backward-incompatible API changes. The last two comments are: "Let me clean up open PRs. I'd like to merge 1.0.x enhancements before cutting the branch." and "Should this be closed?" — indicating no concrete progress.- ttrpc v1.2.8 was released March 2026 and still carries
gogo/protobuf v1.3.2. Themainbranch also still carries it. containerd/typeurl/v2: No open issues for gogo removal.google/cadvisor: No active issues for gogo removal.etcd: etcd's gogo usage is deeply embedded in its wire protocol; no specific removal issue found.
Prognosis: 🔴 Long-term. Requires a ttrpc v2 (breaking change) and coordinated updates across containerd, cadvisor, and etcd. No active development toward this goal.
| Module | Key blocker | Blocker status | Prognosis |
|---|---|---|---|
github.com/google/btree |
etcd | ✅ Removed on main | Next etcd release |
github.com/davecgh/go-spew |
k/k itself via k8s.io/utils/dump.ForHash (kubelet checkpoints, controller hashes) |
🔴 Load-bearing in k/k; testify master removed it but k/k design change needed | Requires ForHash replacement + checkpoint migration |
github.com/mailru/easyjson |
kube-openapi | 🟡 Open PR #590 (by dims) | Needs LGTM from sttts |
gopkg.in/yaml.v3 |
testify + many | 🟡 Open PR testify#1772 | Medium-term; many upstreams |
golang.org/x/exp |
antlr4-go, cel-go | 🟡 Open PR antlr#15 | Medium-term; cel-go has no plan |
github.com/gogo/protobuf |
containerd/ttrpc | 🔴 Stalled (ttrpc#97) | Long-term; needs ttrpc v2 |
github.com/json-iterator/go |
prometheus, kube-openapi, SMD | 🔴 Blocked on Go 1.26 json/v2 | Long-term (Go 1.26+) |
github.com/modern-go/concurrent |
(via json-iterator) | 🔴 Same as json-iterator | Long-term (Go 1.26+) |
github.com/modern-go/reflect2 |
(via json-iterator) | 🔴 Same as json-iterator | Long-term (Go 1.26+) |
github.com/golang/protobuf |
grpc-go | 🔴 Explicit won't-fix in v1.x | Indefinite (needs grpc v2) |
-
kube-openapi PR #590 — The PR dropping
mailru/easyjsonwas filed by dims and is waiting for review. Pingsttts,Jefftree, orapelisseto get a LGTM. Once merged and a kube-openapi snapshot is vendored into Kubernetes,easyjsonleaves the vendor tree. -
Watch for testify release — testify
masterhas removedgo-spew. Subscribe to testify releases for a new tag. Once released, a bump in Kubernetes would removego-spewfrom many (but not all) transitive paths. -
Watch for next etcd release — etcd
mainhas removedgoogle/btree. The next minor/patch release after v3.6.10 should carry this fix.
-
antlr4-go PR #15 — Remove
golang.org/x/expin antlr4-go. The PR replacesx/exp/sliceswith stdlibslices. Two prior attempts were closed; this one has been open longer. A comment or review from the Kubernetes community would help. Note:cel-goalso carriesx/expas an indirect dep, and has no open issues or PRs about removing it — this would need a separate upstream conversation. -
testify PR #1772 — Switch yaml to
go.yaml.in/yaml/v3. This would begin clearing thegopkg.in/yaml.v3transitive chain but many other upstreams would still need to follow.
-
gogo/protobuf— Track ttrpc#97. The containerd ecosystem moving to a ttrpc v2 that usesgoogle.golang.org/protobufis the prerequisite. This likely needs to be driven from within the containerd project. -
json-iterator/modern-go— Track golang/go#71497 (stdlibencoding/json/v2). structured-merge-diff will migrate once json/v2 ships in Go 1.26 (earliest: early 2027). prometheus/client_golang will be harder — they value json-iterator for scrape performance. -
golang/protobuf— No action available. grpc-go has explicitly said they will not remove it in v1.x. Track the grpc-go v2 announcement when/if it happens.
To reproduce this analysis for a future Kubernetes commit:
# 1. Find all unwanted modules still in vendor (exact match)
python3 -c "
import json, re
with open('hack/unwanted-dependencies.json') as f:
data = json.load(f)
unwanted = set(data['spec']['unwantedModules'].keys())
vendored = set()
with open('vendor/modules.txt') as f:
for line in f:
m = re.match(r'^# (\S+) ', line)
if m:
vendored.add(m.group(1))
found = sorted(unwanted & vendored)
for mod in found:
print(mod, '--', data['spec']['unwantedModules'][mod])
"
# 2. For each module, check current vendor version of primary blockers
grep '^# ' vendor/modules.txt | grep '<blocker-module>'
# 3. For each blocker, check their latest release go.mod
curl -s "https://raw.githubusercontent.com/<org>/<repo>/refs/tags/<version>/go.mod" | grep '<unwanted-module>'
# 4. Check main/master branch of blocker
curl -s "https://raw.githubusercontent.com/<org>/<repo>/main/go.mod" | grep '<unwanted-module>'
# 5. Search for upstream issues/PRs
gh search issues --repo <org>/<repo> "<unwanted-module-name>" --json number,title,state,url
gh search prs --repo <org>/<repo> "<unwanted-module-name>" --json number,title,state,urlThe following prompt, given to Claude Code in the root of the Kubernetes repository, will reproduce an equivalent analysis:
I want a detailed status report on the unwanted dependencies still present in the
Kubernetes vendor/ directory, similar to a previous analysis done in April 2026.
Here is the full workflow to follow:
### Step 1 — Find all unwanted modules still vendored (exact match, no prefix false-positives)
Run this Python snippet:
python3 -c "
import json, re
with open('hack/unwanted-dependencies.json') as f:
data = json.load(f)
unwanted = set(data['spec']['unwantedModules'].keys())
vendored = set()
with open('vendor/modules.txt') as f:
for line in f:
m = re.match(r'^# (\S+) ', line)
if m:
vendored.add(m.group(1))
found = sorted(unwanted & vendored)
print(f'Still vendored ({len(found)}):')
for mod in found:
print(f' {mod} ({data[\"spec\"][\"unwantedModules\"][mod]})')
"
### Step 2 — For each remaining module, identify which direct dependencies bring it in
Read hack/unwanted-dependencies.json → status.unwantedReferences. This maps
each unwanted module to the list of our direct dependencies that pull it in.
### Step 3 — Determine current vendor versions of all primary blockers
grep '^# ' vendor/modules.txt | grep -E '<list of primary blockers>'
### Step 4 — For each primary blocker at its current vendored version, check whether
the unwanted module is in its go.mod
curl -s "https://raw.githubusercontent.com/<org>/<repo>/refs/tags/<version>/go.mod" \
| grep '<unwanted-module>'
### Step 5 — Check the blocker's main/master branch for in-progress removal
curl -s "https://raw.githubusercontent.com/<org>/<repo>/main/go.mod" \
| grep '<unwanted-module>'
### Step 6 — Find the latest release of each blocker (to catch cases where main is
ahead of what we vendor)
gh api repos/<org>/<repo>/releases/latest --jq '.tag_name'
Then check that latest release's go.mod too.
### Step 7 — Search for open issues and PRs in upstream repos about removal
For each blocker repo, run:
gh search issues --repo <org>/<repo> "<short-name-of-unwanted-module>" \
--limit 5 --json number,title,state,url
gh search prs --repo <org>/<repo> "<short-name-of-unwanted-module>" \
--limit 5 --json number,title,state,url
Fetch the body and latest comments of the most relevant results:
gh issue view <number> --repo <org>/<repo> --json title,state,body,comments
### Step 8 — Compile the report
Write a 6-7 page markdown to ~/notes/k8s-unwanted-deps-<YYYY-MM>.md with:
1. Background section explaining hack/unwanted-dependencies.json
2. Table of all currently-vendored unwanted modules
3. Per-module section for each one covering:
- Blocklist rationale
- Primary upstream blockers (from status.unwantedReferences)
- Upstream status (latest release still has it? main/master removed it?)
- Known open issues and PRs in upstream repos
- Prognosis (near / medium / long term)
4. Summary table with prognosis column
5. Recommended actions (immediate / medium-term / long-term)
6. Methodology section with reproducible shell commands
7. This appendix prompt
Use ✅ 🟡 🔴 to indicate status (done-unreleased / in-progress / blocked).
### Key repos to check for each module (from the April 2026 analysis):
- google/btree → etcd-io/etcd (server/v3)
- go-spew → stretchr/testify (primary), prometheus/common, go-openapi/*
- yaml.v3 → stretchr/testify (primary), same long tail as go-spew
- mailru/easyjson → kubernetes/kube-openapi → go-openapi/jsonpointer, go-openapi/swag
- json-iterator + modern-go → prometheus/client_golang, kubernetes/kube-openapi,
kubernetes-sigs/structured-merge-diff
- gogo/protobuf → containerd/ttrpc, containerd/typeurl, google/cadvisor,
etcd-io/etcd (server/v3)
- golang/protobuf → grpc/grpc-go (primary), containerd, etcd, konnectivity-client
- golang.org/x/exp → antlr4-go/antlr, google/cel-go,
grpc-ecosystem/go-grpc-middleware
After writing the file, push it to a GitHub Gist using:
gh gist create ~/notes/k8s-unwanted-deps-<YYYY-MM>.md \
--public \
--desc "Kubernetes unwanted vendor dependencies status — <Month YYYY>"
Return the gist URL.