In Kubernetes, kubectl printed a 'Warning:' line and still created the object - what happened at admission?
answer
- advisory, not a verdict
- response header, not response body
- warn-code 299, printed to stderr
- the object exists afterwards
- denial is a failed status instead
basics
~20 sA warning is advisory only. The API server returned it in an HTTP Warning response header alongside a response that succeeded, so the object was admitted. Only a denial, which comes back as a failed status, stops the write.
solid answer
~50 sAdmission has two ways to speak to the client, and they are not the same thing. A denial comes back as a failed API response carrying a status message, and `kubectl` renders it as `Error from server (...): ... denied the request: <message>`; nothing is persisted. A warning rides in an HTTP `Warning` response header with warn-code 299, attached to a response that otherwise succeeded, and `kubectl` prints it to stderr as `Warning: <text>`. Warnings never change the outcome - the object was admitted exactly as submitted. That is what `validationActions: [Warn]` on a ValidatingAdmissionPolicyBinding produces, and what a validating webhook does when it returns `warnings` alongside `allowed: true`. Two practical consequences: warnings go to stderr, so a CI step capturing only stdout loses them, and `kubectl --warnings-as-errors` is what you use if you want a pipeline to exit non-zero on them.
go deeper
Be ready to say what a 'Warning:' line from kubectl means: the server accepted the request and is telling you something anyway. If you are unsure whether the object exists, check with kubectl get.
An interviewer expects you to name the transport: warnings arrive in HTTP Warning response headers with code 299, denials come back as a failed status in the response body. Warnings never change the outcome.
Show that you know where warnings get lost - stderr, non-interactive applies, JSON output - and that a warning nobody reads is the same as no signal at all. Know that --warnings-as-errors exists.
Own what your platform does with advisory signals. If every apply prints warnings that never change anything, you have trained an estate to ignore the exact channel you will need when something urgent has to reach a developer.
## Two channels out of admission Every write to the Kubernetes API server passes through admission before anything is persisted. Admission has exactly two ways to speak back to the client that sent the request, and confusing them is the most common beginner mistake in this area. ### The denial channel A denial is a failed API response. The server returns a status object carrying a human-readable `message` and a reason that determines the HTTP status code. `kubectl` renders it roughly as: `Error from server (Invalid): error when creating "deploy.yaml": ... denied the request: <message>` Nothing is written. The `message` string is the entire explanation the developer gets, which is why the wording of it matters so much. For a ValidatingAdmissionPolicy, each validation may set a `reason` - `Invalid` (the default, HTTP 422), `Forbidden`, `Unauthorized` or `RequestEntityTooLarge` - which selects the code the client sees; the message is still the part a person reads. ### The warning channel A warning travels in an HTTP `Warning` response header, in the form `299 - "text"`. It is attached to a response that otherwise proceeded normally: the object was admitted, defaulted and persisted. `kubectl` prints each warning to **stderr**, one per line, prefixed `Warning:`. Three different things produce warnings on this same channel: - A validating admission webhook that sets `warnings` in its `AdmissionResponse`. - A ValidatingAdmissionPolicyBinding whose `validationActions` includes `Warn`; the failing validation's message is emitted as the warning text. - The API server itself, with no policy involved - a request to a deprecated API group/version comes back with a deprecation warning naming the replacement. Because they share one channel, a developer cannot tell from the terminal whether a `Warning:` line came from your policy or from the platform. Naming your rule inside the text is the only thing that distinguishes it. ### Warnings are advisory, and that is the whole point A warning does not alter the decision. The same unchanged rule, bound with `Warn` instead of `Deny`, tells the person doing the apply and lets the change through. That is genuinely useful: the actor at request time is the one person who can still fix the manifest, and reaching them without blocking them is a real outcome, not a half-measure. One nuance that catches people: warnings are **not** proof of success. The header can ride along with a denied response too, so a response may carry both an error status and one or more warnings. The reliable signal is the outcome line (`deployment.apps/api created`), the command's exit code, or a follow-up `kubectl get`. ### A third channel the client never sees `validationActions` also accepts `Audit`. A failure recorded that way lands in the API server's audit event as an annotation (`validation.policy.admission.k8s.io/validation_failure`) and produces **nothing** in the client's output. From the terminal, an audited failure and a clean apply look identical. ### Where warnings get lost This is the part a working engineer needs to internalise. Warnings disappear when: - the apply runs in CI and the job captures only stdout; - the output is machine-consumed (`-o json`, `-o yaml`), where the warning is not part of the document; - the apply is non-interactive, so no human is watching the stream at all; - the command produced many lines and the warning scrolled past. `kubectl --warnings-as-errors` treats warnings received from the server as errors and exits non-zero, which is the lever for making a pipeline notice them. `kubectl apply --dry-run=server` is the cheap way to see what admission would say - it runs the full admission chain, including your policies, and returns the same warnings and denials without persisting anything. ### What to do when you see one Read the text, check whether the object really exists, and treat the warning as a countdown rather than a curiosity: something that warns today is very often the same rule that will refuse tomorrow. If the text does not tell you which object and which field it is complaining about, that is a defect in the rule, not in you. ### Common confusions worth clearing - A warning is not a mutation. If the stored object differs from what you submitted, something patched it; a warning by itself changes nothing. - A warning is not a retry. The request completed. - Absence of a warning is not proof of compliance - the same violation may have been recorded to the audit channel instead.
- Can a warning ever be attached to a request that was denied?Yes. Warnings travel in response headers and can accompany an allowed or a denied response, so the presence of a `Warning:` line tells you nothing about the outcome on its own. Read the result line, the exit code, or `kubectl get`. This is why treating 'I saw a warning' as 'it went through' is unsafe.
- A binding sets validationActions to [Audit] only. What does the developer see?Nothing. The failure is recorded in the API server's audit event as an annotation and never reaches the client; the apply looks completely clean in the terminal. That signal only becomes visible to someone querying audit logs later, so it reaches no one at request time.
- How would you check what admission will say before actually applying?`kubectl apply --dry-run=server` sends the request through the real admission chain, including validating policies and webhooks, and returns the same denial or warnings without persisting anything. Client-side dry-run does not do this - it never reaches the server, so it cannot show you a policy decision.
A denial is the door refusing to open. A warning is a sticker on the door that you read on your way through.
saying these in an interview costs you the question
- Says a warning means the object was rejected
- Assumes warnings appear on stdout with the rest of the output
- Thinks warn mode still blocks something
- Believes only policies emit warnings, not the API server itself
- Treats a warning as evidence the request succeeded