Kubernetes Error Guide: 'strict decoding error: unknown field' — Fix Invalid Manifest Fields
Fix the 'strict decoding error: unknown field' from kubectl and Server-Side Apply: typos, wrong indentation nesting, deprecated fields, and CRD schema mismatches — with a full diagnostic workflow.
- #kubernetes
- #troubleshooting
- #errors
- #manifests
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
strict decoding error: unknown field means the API server (or kubectl in client-side validation) parsed your manifest, found a field it does not recognize for that object’s schema, and refused it. Since strict validation became the default, a misspelled or misplaced key is no longer silently dropped — it is an outright rejection.
You will see it in one of these forms:
error: error validating "deploy.yaml": error validating data: ValidationError(Deployment.spec.template.spec.containers[0]): unknown field "resource" in io.k8s.api.core.v1.Container
Error from server (BadRequest): error when creating "deploy.yaml": Deployment in version "v1" cannot be handled as a Deployment: strict decoding error: unknown field "spec.replias"
The two variants matter: the first (error validating) comes from kubectl’s client-side validation before the request is sent; the second (Error from server (BadRequest)) comes from the API server’s own strict decoder, which Server-Side Apply and newer clients use. Both mean the same thing — a key in your YAML/JSON does not exist in the target resource’s schema at the place you put it.
Symptoms
kubectl apply/createfails immediately withunknown field "<name>"and the object is never created or updated.- The error names the exact field and its schema path, e.g.
Deployment.spec.template.spec.containers[0]. - The same manifest may have “worked” on an older cluster (pre-strict) where unknown fields were dropped silently.
- For CRDs, the field is real for one version but not the one you specified in
apiVersion.
kubectl apply -f deploy.yaml
error: error validating "deploy.yaml": error validating data:
ValidationError(Deployment.spec): unknown field "replias" in io.k8s.api.apps.v1.DeploymentSpec;
if you choose to ignore these errors, turn validation off with --validate=false
Common Root Causes
1. A simple typo in a field name
The most common cause: replias instead of replicas, commands instead of command, enviroment instead of env. The schema path in the error points straight at the offending key.
ValidationError(Deployment.spec): unknown field "replias" in io.k8s.api.apps.v1.DeploymentSpec
Fix the spelling. kubectl explain deployment.spec lists the valid fields at that level.
2. Correct field, wrong nesting / indentation
The field name is spelled right but sits at the wrong level, so the decoder looks for it in a schema where it does not belong. A classic case is putting resources one level too high, or containers under spec instead of spec.template.spec.
spec:
template:
spec:
containers:
- name: app
image: nginx
resources: # WRONG: resources belongs INSIDE the container
limits:
memory: 256Mi
ValidationError(Deployment.spec.template.spec): unknown field "resources" in io.k8s.api.core.v1.PodSpec
resources is a Container field, not a PodSpec field. Move it under the container.
3. Removed or renamed field in a newer API version
A field valid in an older schema was removed or renamed. Common examples: spec.selector shape changes, deprecated serviceAccount (use serviceAccountName), or beta fields that graduated and were renamed on promotion. On upgrade, a once-valid manifest suddenly rejects.
strict decoding error: unknown field "spec.serviceAccount"
Check the current schema with kubectl explain and update the manifest to the supported field.
4. CRD field that exists in a different version
For custom resources, each served version has its own schema. If you set apiVersion: example.com/v1beta1 but the field only exists in v1, strict decoding rejects it.
Error from server (BadRequest): ... strict decoding error: unknown field "spec.newTuning"
Confirm which versions the CRD serves and which contains the field:
kubectl get crd widgets.example.com -o jsonpath='{.spec.versions[*].name}'
kubectl explain widget.spec --api-version=example.com/v1
5. Wrong kind/apiVersion for the block you wrote
A manifest labeled as one kind but carrying another kind’s fields — e.g. a Service spec with containers, or a ConfigMap with spec. The decoder validates against the declared kind, so foreign fields are “unknown.”
ValidationError(Service.spec): unknown field "containers" in io.k8s.api.core.v1.ServiceSpec
Diagnostic Workflow
Step 1: Read the schema path in the error
The parenthetical path — ValidationError(Deployment.spec.template.spec.containers[0]) — tells you exactly which object and level the decoder was validating when it hit the unknown field. That is where to look, not the top of the file.
Step 2: Confirm the valid fields at that level
kubectl explain deployment.spec.template.spec.containers
kubectl explain deployment.spec.template.spec.containers.resources
kubectl explain prints the real schema for the exact path, so you can compare field names and nesting against your YAML.
Step 3: Validate without applying (dry run)
kubectl apply -f deploy.yaml --dry-run=server
Server-side dry run runs the same strict decoder the real apply uses (including CRD schemas and admission), so it reproduces the error safely without mutating anything.
Step 4: For CRDs, check which versions serve the field
kubectl get crd <name>.<group> -o yaml | grep -A3 'name:.*versions\|served'
kubectl explain <resource>.spec --api-version=<group>/<version>
Make sure your apiVersion matches the version whose schema actually contains the field.
Step 5: Diff against a known-good object
kubectl get deployment <existing> -o yaml > good.yaml
diff <(yq '.spec' good.yaml) <(yq '.spec' deploy.yaml)
Comparing your manifest to a live, accepted object of the same kind quickly surfaces a misnamed or misplaced field.
Example Root Cause Analysis
A pipeline that deployed fine last quarter starts failing after a cluster upgrade:
kubectl apply -f worker.yaml
Error from server (BadRequest): error when creating "worker.yaml":
Deployment in version "v1" cannot be handled as a Deployment:
strict decoding error: unknown field "spec.template.spec.containers[0].livenessProbe.timeout"
The schema path points at the container’s livenessProbe. Checking the real schema:
kubectl explain deployment.spec.template.spec.containers.livenessProbe
FIELDS:
timeoutSeconds <integer>
...
The field is timeoutSeconds, not timeout. On the old cluster (before strict decoding was the default) the unknown timeout key was silently dropped, so the probe quietly used the 1-second default and nobody noticed. After the upgrade, strict decoding rejects the whole manifest.
Fix — rename the field:
livenessProbe:
httpGet:
path: /healthz
port: 8080
timeoutSeconds: 5 # was: timeout: 5
kubectl apply -f worker.yaml --dry-run=server # passes
kubectl apply -f worker.yaml
The deployment applies and the probe now gets the intended 5-second timeout — the bug the silent drop had been hiding.
Prevention Best Practices
- Run
kubectl apply --dry-run=server(or--server-side --dry-run=server) in CI on every manifest so unknown fields fail the pipeline, not production. - Use
kubectl explain <kind>.<path>to confirm both the field name and its nesting level before writing it — the schema path is authoritative. - Lint manifests with a schema-aware tool (kubeconform / kubeval against the right Kubernetes version) so typos are caught locally.
- After a cluster upgrade, re-validate manifests that “worked” before — strict decoding surfaces fields that were previously dropped silently and may hide bugs.
- For CRDs, pin
apiVersionto a version whose schema actually contains every field you set, and re-check when the operator is upgraded. - Never reach for
--validate=falseto make the error go away — it skips validation but the field is still ignored, reintroducing the silent-drop bug. See more in Kubernetes & Helm guides.
Quick Command Reference
# Reproduce the error safely against the real (server) decoder
kubectl apply -f manifest.yaml --dry-run=server
# Show valid fields + nesting for the exact schema path in the error
kubectl explain <kind>.<path>
kubectl explain deployment.spec.template.spec.containers.resources
# For CRDs: which versions are served, and the schema per version
kubectl get crd <name>.<group> -o jsonpath='{.spec.versions[*].name}'
kubectl explain <resource>.spec --api-version=<group>/<version>
# Diff your manifest against a known-good live object
kubectl get <kind> <existing> -o yaml > good.yaml
diff <(yq '.spec' good.yaml) <(yq '.spec' manifest.yaml)
# Schema-lint locally in CI (example)
kubeconform -strict -kubernetes-version 1.30.0 manifest.yaml
Conclusion
strict decoding error: unknown field means a key in your manifest does not exist in the target schema at the level you placed it. The usual root causes:
- A typo in the field name (
replias,timeoutvstimeoutSeconds). - A correct field at the wrong nesting level (e.g.
resourcesoutside the container). - A field removed or renamed in a newer API version, surfacing after an upgrade.
- A CRD field that only exists in a different served version than the
apiVersionyou set. - A manifest whose
kinddoes not match the fields it carries.
Read the schema path in the error, confirm the truth with kubectl explain, and reproduce with --dry-run=server. Strict decoding is a feature: the same typo that now fails loudly used to be dropped silently, often hiding a real misconfiguration. For turning a stack of apply errors into a prioritized fix list, the free incident assistant can help.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.