Why does a misspelled key in a Helm values file cause no error, and what makes Helm reject it?
answer
- Values are a map, not a declaration
- Nothing knows which keys templates read
- A missing lookup renders empty, not fatal
- One file in the chart makes it strict
- additionalProperties false, per object
basics
~20 sHelm merges caller values onto chart defaults as an untyped map, so an unknown key is simply added and never read - the template still sees the default and the release installs cleanly. A values.schema.json with additionalProperties: false turns that typo into a failed install.
solid answer
~50 sValues are a plain nested map with no declared shape. Merging a caller's file onto the chart's defaults just adds keys that were not there, and Helm has no idea which key paths the templates actually read. When the template then indexes `.Values.resources.limits.memory` and the caller wrote `resource:`, the lookup yields the default - or nothing, which Go's template engine renders as empty rather than raising - so the chart renders, the manifests apply, and the release reports as deployed with the change silently absent. Two defences exist. A `values.schema.json` at the chart root is validated before rendering, and `additionalProperties: false` on the objects the chart owns makes an undeclared key a hard failure, while `type` and `enum` catch a string where a number belongs. On the template side, Helm's `required` function aborts the render with your own message when a value is empty.
code
json · 20 lines{
"$schema": "https://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"required": ["image"],
"properties": {
"replicaCount": { "type": "integer", "minimum": 1 },
"image": {
"type": "object",
"additionalProperties": false,
"required": ["repository"],
"properties": {
"repository": { "type": "string" },
"tag": { "type": "string" },
"pullPolicy": { "enum": ["Always", "IfNotPresent", "Never"] }
}
},
"resources": { "type": "object" }
}
}go deeper
Remember the failure shape: a mistyped values key does not error, the install succeeds, and your setting simply is not there. Knowing to check the rendered output rather than trusting the exit code is the point.
Explain the mechanism - an untyped map merge, no declared key set, a missing template lookup rendering empty - and name values.schema.json with additionalProperties: false as the thing that turns it into an error.
Show how you close the gap in practice: which objects you close in the schema, where the render runs in CI so validation actually fires, and when a template-side required or fail is the right guard instead.
Frame it as a delivery-safety question. Decide who is accountable for the values contract across your charts and how a silently ignored key stops being a class of production incident rather than one team's postmortem action.
### Why nothing complains A chart's values are an untyped, arbitrarily nested map. `values.yaml` supplies one; a caller supplies another through a file or a command-line override; Helm coalesces them into the single map that the renderer exposes as `.Values`. Coalescing is a merge over maps, not a check against a declaration. There is no list of legal keys anywhere in a chart unless the author writes one, and Helm cannot infer that list from the templates - the templates are text, and a key path can be constructed dynamically, so static extraction is not on the table. So the misspelling survives the merge as data. `.Values.resource` now exists and is a perfectly valid entry that nothing ever reads. Meanwhile the template still asks for `.Values.resources`, gets whatever `values.yaml` defaulted it to, and renders that. If the chart defaulted it to an empty map, the lookup returns nil, and Go's template engine treats a missing map key as an ordinary empty value rather than an error - so you get an empty string or a dropped block, not a stack trace. The result is the worst possible failure shape: `helm upgrade` exits zero, the release status is deployed, the manifests apply, and the only evidence is the object in the cluster not having the property you thought you set. For an order-checkout API chart published to an OCI registry by CI, a values file that said `resource:` instead of `resources:` shipped pods with no memory limit; it survived 6 days and two more upgrades before a node ran out of memory under a promotion spike, and every layer in the delivery path had reported success the whole time. ### The chart-side fix: values.schema.json `values.schema.json` is a JSON Schema document sitting at the chart root beside `values.yaml`, packaged into the `.tgz` with everything else. When present, Helm validates the coalesced values against it before rendering, and a failure aborts the command with the failing property paths named. It is the only mechanism that converts an unknown key into an error, and the keyword that does it is `additionalProperties: false` on each object whose key set the chart owns - `type`, `enum`, `minimum` and `pattern` catch the other family of mistakes, a wrong type or an out-of-range value. Three details decide whether it actually helps. First, `additionalProperties: false` is per object: setting it at the root constrains the top level only, and every nested object you want protected needs its own. Second, the schema validates the *merged* values, so a property the schema marks `required` is satisfied by anything the caller supplies - it does not have to be in values.yaml. Third, the JSON Schema keyword `required` and Helm's template function `required` are unrelated things that share a name: the keyword asserts a property is present in the values document, and the function aborts a render when a value is empty at the point it is used. Confusing them is a standard interview tell. ### The template-side fix: required and fail Schemas describe the shape of the values document. Some invariants are not shape: a value that must be non-empty only when another feature is enabled, or a check against what the target cluster supports through `.Capabilities`. Those live in the template. Helm adds `required "message" VALUE`, which returns the value or aborts the whole render with your message, and `fail "message"`, which aborts unconditionally - useful inside a conditional that detects an impossible combination. They fire during rendering rather than before it, so they catch things a schema cannot, at the cost of a later and less precise error. ### Where the typo is caught With a schema present, the same validation runs on `helm install`, `helm upgrade`, `helm template` and `helm lint`, so a rendering job in CI catches a bad values file before anything reaches a cluster. Without one, none of those commands will ever object - including `helm lint`, which is frequently and wrongly assumed to check values keys. The same is true of a mistyped path in a `--set` override: it lands in the map like any other key and is just as silent. ### What a good answer adds The honest caveat is that a schema only protects the keys the author bothered to declare, and a schema that lags the templates gives false confidence. The practical bar for an internal chart is: declare the objects you own, close them with `additionalProperties: false`, type the leaves, and render a representative values file in CI so the validation actually runs on every change.
- Does `helm lint` catch a misspelled values key on its own?Only if the chart ships a `values.schema.json`, because lint's values check is that schema. With no schema, lint inspects chart structure and template rendering and has no opinion about which values keys are legal, so a misspelling passes lint exactly as it passes install.
- What is the difference between JSON Schema's `required` and Helm's `required` function?The schema keyword lists properties that must be present in the values document, and Helm checks it before rendering - satisfied by a default or by anything the caller passes. The `required` function is a template function evaluated during rendering: it returns the value, or aborts the whole render with the message you wrote when the value is empty. Different stages, different failure text, unrelated mechanisms.
- Why is `additionalProperties: false` at the top level often not enough?The keyword applies to the object it is written on. Closing the root stops an unknown top-level key but says nothing about the inside of `image` or `resources`, where most typos actually happen. Every object whose key set the chart owns needs its own `additionalProperties: false`, and objects you deliberately pass through untouched should stay open.
It is like passing extra query parameters to an HTTP endpoint that ignores what it does not recognise: the request succeeds, the response looks fine, and the setting you meant to change was never applied.
saying these in an interview costs you the question
- Claims Helm errors on values keys a chart never declares
- Says helm lint catches typos without a schema
- Believes a --set with a wrong path fails fast
- Confuses JSON Schema required with the required function
- Expects a nil value lookup to abort the render
- Thinks a root-level additionalProperties protects nested objects