
troubleshooting
CreateContainerConfigError and CreateContainerError: the four messages
The kubelet could not build the container config — almost always a missing ConfigMap or Secret key. The message names the object that is missing.
CreateContainerConfigError means the image was pulled successfully and the
kubelet then failed to assemble the container’s configuration. In almost every
real case that is an environment variable pointing at a ConfigMap or Secret that
does not exist, or exists without the key you asked for.
The useful part is that the kubelet puts the exact object name in the message, so
this error tells you the answer rather than hinting at it. Most people never read
it because kubectl get pods truncates at the reason.
The fastest check
kubectl get pod <pod> -o jsonpath='{.status.containerStatuses[*].state.waiting.reason}{"\t"}{.status.containerStatuses[*].state.waiting.message}{"\n"}'
CreateContainerConfigError configmap "nope" not found
That is the whole diagnosis. Across a namespace:
kubectl get pods -o custom-columns='NAME:.metadata.name,REASON:.status.containerStatuses[*].state.waiting.reason,MESSAGE:.status.containerStatuses[*].state.waiting.message'
The four messages, verified on 1.36
These are the actual strings, reproduced on a Kubernetes 1.36.1 cluster. Each maps to exactly one mistake.
| Message | What you wrote |
|---|---|
configmap "X" not found | configMapKeyRef or configMapRef naming a ConfigMap that does not exist in this namespace |
secret "X" not found | secretKeyRef or secretRef naming a Secret that does not exist in this namespace |
couldn't find key K in Secret ns/X | The Secret exists. The key does not |
container has runAsNonRoot and image will run as root | securityContext.runAsNonRoot: true on an image whose USER is root or unset |
The first three are the same mistake in three costumes, and the namespace is the
usual culprit: a ConfigMap reference resolves in the pod’s own namespace only.
There is no cross-namespace form, and there is no error that says “wrong namespace”
— you get not found.
The fourth one surprises people because it looks like a runtime failure and is not.
The kubelet resolves runAsNonRoot by inspecting the image’s configured user
before it creates the container, so an image that does not declare a non-root
USER fails config generation. Setting runAsUser to a numeric UID satisfies it;
setting runAsNonRoot: true alone does not, because the kubelet has nothing to
check against.
Where in the startup path it fails
The reason string is precise about which step failed, and the steps run in a fixed order. Knowing the order means the reason alone tells you what already succeeded.
That has a practical consequence worth internalising: CreateContainerConfigError
proves the image pull worked. If you are looking at this error, the registry, the
credentials and the tag are all fine, and you can stop checking them. When the pull
is the problem you get a different reason entirely, covered in
ImagePullBackOff and ErrImagePull.
What does not produce this error
Two things get attributed to CreateContainerConfigError constantly and do not
cause it. Both were tested on 1.36.1.
A missing ConfigMap or Secret used as a volume. This does not reach config
generation at all — the mount fails first, so the pod sits in ContainerCreating
with a FailedMount event:
Warning FailedMount 0s (x7 over 32s) kubelet MountVolume.SetUp failed for volume "cfg" : configmap "absent-cm" not found
Same root cause, different reason, and crucially a different place to look: the
events, not the container status. A pod stuck in ContainerCreating with no
container status message is nearly always a volume.
A command or binary that does not exist. That is RunContainerError, which
happens one step later — the config was built fine and the runtime refused to start
the process:
RunContainerError failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: exec: "/no/such/binary": stat /no/such/binary: no such file or directory
The wall of runtime text is unpleasant but the tail of it is the answer, and it is
always the tail. The same is true for a hostPath mounted at a path whose parent
is a file rather than a directory, which produces the same reason with a
securejoin message instead.
CreateContainerError — without Config — is the third of the family and the
rarest. It comes from the runtime rejecting an otherwise valid config, most often a
container name still in use after an abrupt kubelet restart. If you see it and the
message mentions a name conflict, it usually clears itself on the next sync.
Fixing it
Name the object the message named. The error is not generic:
kubectl get configmap <name>
kubectl get secret <name> -o jsonpath='{.data}' | tr ',' '\n'
The second one lists the keys present without printing the values, which is what
you need for couldn't find key.
Make optional references optional. If a config value genuinely may be absent, say so, and the container starts without it instead of blocking:
env:
- name: FEATURE_FLAG
valueFrom:
configMapKeyRef:
name: features
key: beta
optional: true
This is the fix for the classic ordering failure, where a Helm chart creates the Deployment before the ConfigMap and the first pod dies on a race that resolves itself thirty seconds later.
Check the namespace before the name. A ConfigMap that exists in default and a
pod running in prod produce not found, which reads like a typo and is not.
Preventing it
- The pod does not retry into a backoff for this. Unlike a crash loop, the kubelet keeps retrying config generation at the sync interval, so a pod fixed by creating the missing ConfigMap recovers on its own without a restart. You do not need to delete the pod, and deleting it hides how long it was broken.
- Reference keys, not whole objects, where you can.
configMapKeyReffails with a message naming the key.envFromwith a missing object fails with a message naming only the object, which is less information for the same mistake. - Treat
runAsNonRootas an image contract. If your base image does not set a numericUSER, the policy will fail every pod that uses it, and the failure arrives at deploy time rather than build time.
For the errors that come after this one — the container starts and then dies — see how to fix CrashLoopBackOff, and for reading the STATUS column that reports all of these, how kubectl computes a pod’s STATUS.
The waiting-state message is the answer, and it is the field most tools hide. KubeGlance is a native Kubernetes client for iPhone and iPad, with a full Mac app on the same core — container states and their messages without a jsonpath expression.
Get KubeGlanceThe Kubernetes dashboard that fits in your pocket
KubeGlance is a native Kubernetes client for iPhone and iPad — the real dashboard, not a companion — with a full Mac app on the same core. Pods, workloads, logs and events, straight from your kubeconfig.
Download KubeGlance

