kubernetes/kubernetes · error

err.Error()

Error message

err.Error()

What it means

500 Internal Server Error at server.go:317, the fallback inside writeResponse when json.NewEncoder(w).Encode(review) fails (the encode error is logged at server.go:315 and then its .Error() string is sent to the client). Encoding fails when the AdmissionResponse cannot be JSON-serialized — typically because a nested Status or Warning carries a non-marshalable field, or the response was already partially written and the connection broke.

Source

Thrown at staging/src/k8s.io/pod-security-admission/cmd/webhook/server/server.go:317

	}

	if err := s.delegate.CompleteConfiguration(); err != nil {
		return nil, fmt.Errorf("configuration error: %w", err)
	}
	if err := s.delegate.ValidateConfiguration(); err != nil {
		return nil, fmt.Errorf("invalid configuration: %w", err)
	}

	return s, nil
}

func writeResponse(w http.ResponseWriter, review *admissionv1.AdmissionReview) {
	// Webhooks should always respond with a 200 HTTP status code when an AdmissionResponse can be sent.
	// In an error case, the true status code is captured in the response.result.code
	if err := json.NewEncoder(w).Encode(review); err != nil {
		klog.ErrorS(err, "Failed to encode response")
		// Unable to send an AdmissionResponse, fall back to an HTTP error.
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// parseTimeout parses the given HTTP request URL and extracts the timeout query parameter
// value if specified by the user.
// If a timeout is not specified the function returns false and err is set to nil
// If the value specified is malformed then the function returns false and err is set
func parseTimeout(req *http.Request) (time.Duration, bool, error) {
	value := req.URL.Query().Get("timeout")
	if value == "" {
		return 0, false, nil
	}

	timeout, err := time.ParseDuration(value)
	if err != nil {
		return 0, false, fmt.Errorf("invalid timeout query: %w", err)
	}

View on GitHub (pinned to b882c60b40)

Solutions

  1. Check the webhook logs for the 'Failed to encode response' line — it carries the underlying encode error.
  2. Verify the AdmissionResponse returned by the delegate is a plain admissionv1.AdmissionResponse without custom/non-serializable fields.
  3. Ensure the apiserver's webhook timeout (timeoutSeconds) is not shorter than the time the webhook needs to respond.
  4. If the client is closing early, investigate apiserver-side cancellation (context deadline, request aborted).
  5. Buffer the response before writing (encode to a bytes.Buffer first, then w.Write) so a mid-encode failure does not leave a half-written body — though the upstream code encodes directly to w.

Example fix

// before: encode straight to the ResponseWriter; a mid-encode error
// leaves a half-written body and only then calls http.Error
func writeResponse(w http.ResponseWriter, review *admissionv1.AdmissionReview) {
    if err := json.NewEncoder(w).Encode(review); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

// after: buffer so encode failures surface before any bytes are sent
func writeResponse(w http.ResponseWriter, review *admissionv1.AdmissionReview) {
    buf := &bytes.Buffer{}
    if err := json.NewEncoder(buf).Encode(review); err != nil {
        klog.ErrorS(err, "Failed to encode response")
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    w.Header().Set("Content-Type", "application/json")
    w.Write(buf.Bytes())
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Server-side hardening: encode into a buffer first so an encode failure
// never leaves a half-written response.
func safeWriteResponse(w http.ResponseWriter, review *admissionv1.AdmissionReview) error {
    buf := &bytes.Buffer{}
    if err := json.NewEncoder(buf).Encode(review); err != nil {
        return err
    }
    w.Header().Set("Content-Type", "application/json")
    _, err := w.Write(buf.Bytes())
    return err
}

Type guard

// Ensure the response is JSON-serializable before touching the wire.
func isSerializable(review *admissionv1.AdmissionReview) error {
    if _, err := json.Marshal(review); err != nil {
        return fmt.Errorf("response not serializable: %w", err)
    }
    return nil
}

Try / catch

// Server: log encode errors and fall back cleanly; never panic.
if err := safeWriteResponse(w, review); err != nil {
    klog.ErrorS(err, "Failed to encode response")
    if !headersWritten(w) {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

Prevention

When it happens

Trigger: json.Encode returns an error mid-write (connection closed by client, response already started); a response field of an unsupported Go type slipped into the AdmissionReview; extremely large response objects; a Status with custom fields not understood by the encoder.

Common situations: Client (apiserver) cancelling the webhook call right after the response starts streaming; a custom evaluator that injects non-serializable data; webhook behind a proxy with a short response timeout; concurrent close during graceful shutdown.

Related errors


AI-assisted analysis of kubernetes/kubernetes@b882c60b40 (2026-08-07). Data as JSON: /api/errors/779890ce35de0c01. Report an issue: GitHub.