Summary
Starting in kubernetes==36.0.0, clients configured via config.load_incluster_config() send HTTP requests without an Authorization header. Every API call from inside a pod therefore returns 401 Unauthorized. The same code path works correctly in 35.0.0 with the same service account token, the same cluster, and the same network setup.
Reproducer
Run the following inside an in-cluster pod (e.g. an EKS pod with a mounted service account token):
from kubernetes import config, client
import urllib3
# Patch urllib3 to trace requests
orig_urlopen = urllib3.PoolManager.urlopen
def traced(self, method, url, **kw):
print("method:", method)
print("url:", url)
print("headers:", dict(kw.get("headers") or {}))
return orig_urlopen(self, method, url, **kw)
urllib3.PoolManager.urlopen = traced
config.load_incluster_config()
api_client = client.ApiClient()
client.VersionApi(api_client).get_code()
Observed output (kubernetes 36.0.0)
method: GET
url: https://172.20.0.1:443/version/
headers: {'Accept': 'application/json', 'User-Agent': 'OpenAPI-Generator/36.0.0/python', 'Content-Type': 'application/json'}
Note: no Authorization header is set. The server then responds 401 Unauthorized.
Expected output (kubernetes 35.0.0, same code, same token)
method: GET
url: https://172.20.0.1:443/version/
headers: {'Accept': 'application/json', 'Authorization': 'Bearer <redacted>', 'User-Agent': 'OpenAPI-Generator/35.0.0/python', 'Content-Type': 'application/json'}
Returns 200 OK.
Confirmed not a token / RBAC / network issue
The same SA token works in every other context:
curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization: Bearer $TOKEN" https://172.20.0.1:443/version → 200.
urllib3.PoolManager().request(...) directly with the same token → 200.
- Pinning
kubernetes<36 (resolves to 35.0.0) with no other changes → 200.
The token itself is valid: decoded JWT shows iat ~15 minutes ago, exp ~365 days out, aud=['https://kubernetes.default.svc']. RBAC is correct — curl with the same token returns 200 against /api/v1/namespaces/{ns}/pods.
Workarounds verified
- Pin
kubernetes<36 (resolves to 35.0.0).
- Use
urllib3 directly with the bearer token from /var/run/secrets/kubernetes.io/serviceaccount/token.
- Manually set the header on the
ApiClient configuration after load_incluster_config().
Suspected area
The BearerToken auth setting plumbing in kubernetes/client/api_client.py (or whatever was touched between 35.0.0 and 36.0.0 along the auth / ApiClient path). It looks like load_incluster_config() is populating the configuration with a key that the generated auth_settings=["BearerToken"] lookup in ApiClient no longer reads, so the header is silently dropped.
Environment
kubernetes (PyPI): 36.0.0
urllib3: 1.26.12
- Python:
3.11
- Cluster: EKS, Kubernetes
1.32.13-eks
- Discovered: 2026-05-22
Impact
Any production system using kubernetes-client/python from inside a Kubernetes pod will start failing the moment pip resolves to 36.0.0. This includes operators, controllers, and any in-cluster tooling that calls load_incluster_config(). We hit it on a DynamicClient(api_client) discovery call at startup; consumers calling any other API will hit the same 401.
Note
36.0.0 was released approximately 2026-05-21 (one day before this report). I did a quick gh issue list ... --search "401 in-cluster" and didn't find a clean duplicate, but happy to consolidate if maintainers identify one.
Summary
Starting in
kubernetes==36.0.0, clients configured viaconfig.load_incluster_config()send HTTP requests without anAuthorizationheader. Every API call from inside a pod therefore returns401 Unauthorized. The same code path works correctly in35.0.0with the same service account token, the same cluster, and the same network setup.Reproducer
Run the following inside an in-cluster pod (e.g. an EKS pod with a mounted service account token):
Observed output (kubernetes 36.0.0)
Note: no
Authorizationheader is set. The server then responds401 Unauthorized.Expected output (kubernetes 35.0.0, same code, same token)
Returns
200 OK.Confirmed not a token / RBAC / network issue
The same SA token works in every other context:
curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization: Bearer $TOKEN" https://172.20.0.1:443/version→200.urllib3.PoolManager().request(...)directly with the same token →200.kubernetes<36(resolves to35.0.0) with no other changes →200.The token itself is valid: decoded JWT shows
iat~15 minutes ago,exp~365 days out,aud=['https://kubernetes.default.svc']. RBAC is correct —curlwith the same token returns200against/api/v1/namespaces/{ns}/pods.Workarounds verified
kubernetes<36(resolves to35.0.0).urllib3directly with the bearer token from/var/run/secrets/kubernetes.io/serviceaccount/token.ApiClientconfiguration afterload_incluster_config().Suspected area
The
BearerTokenauth setting plumbing inkubernetes/client/api_client.py(or whatever was touched between35.0.0and36.0.0along the auth /ApiClientpath). It looks likeload_incluster_config()is populating the configuration with a key that the generatedauth_settings=["BearerToken"]lookup inApiClientno longer reads, so the header is silently dropped.Environment
kubernetes(PyPI):36.0.0urllib3:1.26.123.111.32.13-eksImpact
Any production system using
kubernetes-client/pythonfrom inside a Kubernetes pod will start failing the moment pip resolves to36.0.0. This includes operators, controllers, and any in-cluster tooling that callsload_incluster_config(). We hit it on aDynamicClient(api_client)discovery call at startup; consumers calling any other API will hit the same401.Note
36.0.0was released approximately 2026-05-21 (one day before this report). I did a quickgh issue list ... --search "401 in-cluster"and didn't find a clean duplicate, but happy to consolidate if maintainers identify one.