kubernetes/kubernetes · warning
unable to process a request with a non-json content type
Error message
unable to process a request with a non-json content type
What it means
400 Bad Request at server.go:197-201 returned when Content-Type is anything other than exactly 'application/json' (strict string equality on r.Header.Get). The check is case- and whitespace-sensitive: 'application/json; charset=utf-8', 'Application/JSON', or a missing header all fail. Content type is verified after body size but before decoding.
Source
Thrown at staging/src/k8s.io/pod-security-admission/cmd/webhook/server/server.go:200
defer r.Body.Close()
limitedReader := &io.LimitedReader{R: r.Body, N: maxRequestSize}
if body, err = ioutil.ReadAll(limitedReader); err != nil {
logger.Error(err, "unable to read the body from the incoming request")
http.Error(w, "unable to read the body from the incoming request", http.StatusBadRequest)
return
}
if limitedReader.N <= 0 {
logger.Error(err, "unable to read the body from the incoming request; limit reached")
http.Error(w, fmt.Sprintf("request entity is too large; limit is %d bytes", maxRequestSize), http.StatusRequestEntityTooLarge)
return
}
// verify the content type is accurate
if contentType := r.Header.Get("Content-Type"); contentType != "application/json" {
err = fmt.Errorf("contentType=%s, expected application/json", contentType)
logger.Error(err, "unable to process a request with an unknown content type", "type", contentType)
http.Error(w, "unable to process a request with a non-json content type", http.StatusBadRequest)
return
}
v1AdmissionReviewKind := admissionv1.SchemeGroupVersion.WithKind("AdmissionReview")
reviewObject, gvk, err := codecs.UniversalDeserializer().Decode(body, &v1AdmissionReviewKind, nil)
if err != nil {
logger.Error(err, "unable to decode the request")
http.Error(w, "unable to decode the request", http.StatusBadRequest)
return
}
if *gvk != v1AdmissionReviewKind {
logger.Info("Unexpected AdmissionReview kind", "kind", gvk.String())
http.Error(w, fmt.Sprintf("unexpected AdmissionReview kind: %s", gvk.String()), http.StatusBadRequest)
return
}
review, ok := reviewObject.(*admissionv1.AdmissionReview)
if !ok {
logger.Info("Failed admissionv1.AdmissionReview type assertion")View on GitHub (pinned to b882c60b40)
Solutions
- Set the header to exactly application/json with no parameters: req.Header.Set("Content-Type", "application/json").
- If using the apiserver, ensure you are not forcing protobuf serialization onto this webhook — PodSecurity webhook speaks JSON only.
- Check intermediaries (mesh, ingress, WAF) for header rewriting and pin Content-Type end-to-end.
- When testing, pass -H 'Content-Type: application/json' explicitly to curl.
Example fix
// before: client appends a charset, server rejects
req.Header.Set("Content-Type", "application/json; charset=utf-8")
// after: exact match the webhook expects
req.Header.Set("Content-Type", "application/json") Defensive patterns
Strategy: validation
Validate before calling
// Caller: set Content-Type exactly. The webhook uses strict equality
// against "application/json" (no charset, no parameters).
req.Header.Set("Content-Type", "application/json") // exact
if ct := req.Header.Get("Content-Type"); ct != "application/json" {
return fmt.Errorf("bad content type %q", ct)
} Type guard
func isExactJSONContentType(ct string) bool { return ct == "application/json" } Try / catch
// Client: 400 here is a programmer error; correct the header and resend.
if resp.StatusCode == 400 && strings.Contains(body, "non-json content type") {
return fmt.Errorf("set Content-Type: application/json exactly (no charset)")
} Prevention
- Always set Content-Type with req.Header.Set, never req.Header.Add, to avoid duplicates.
- Pin intermediaries to forward Content-Type unchanged.
- Add an integration test asserting the exact header value.
- When using curl, pass -H 'Content-Type: application/json'.
When it happens
Trigger: Calling the webhook with Content-Type application/json; charset=utf-8 (common from some HTTP clients); sending protobuf or yaml; a client defaulting to text/plain; a proxy that rewrites or strips Content-Type; the apiserver misconfigured to call a webhook with a non-JSON content type.
Common situations: Using a generic HTTP client or gRPC-gateway that appends a charset parameter; manually testing with curl without -H 'Content-Type: application/json'; a service mesh/ingress normalizing headers; mismatch between the webhook's expected format and what a custom admission sender emits.
Related errors
- request body is empty
- request entity is too large; limit is %d bytes
- unable to decode the request
- unexpected AdmissionReview kind: %s
- internal server error
AI-assisted analysis of kubernetes/kubernetes@b882c60b40 (2026-08-07).
Data as JSON: /api/errors/66fd32fceb4d9c25.
Report an issue: GitHub.