If you have been running a Helm chart for a while and using
--reuse-values to carry your previous configuration forward on upgrades, you may have hit an error like the one below after bumping to a new chart version:
Error: UPGRADE FAILED: template: acme-web-proxy/templates/deployment.yaml:22:15: executing "acme-web-proxy/templates/deployment.yaml" at <include "acme-web-proxy.podAnnotations" .>: error calling include: template: acme-web-proxy/templates/_helpers.tpl:41:71: executing "acme-web-proxy.podAnnotations" at <include (print $.Template.BasePath "/configmap.yaml") .>: error calling include: template: acme-web-proxy/templates/configmap.yaml:18:6: executing "acme-web-proxy/templates/configmap.yaml" at <include "acme-web-proxy.metrics.config" .>: error calling include: template: acme-web-proxy/templates/configmap.yaml:34:25: executing "acme-web-proxy/templates/configmap.yaml" at <.Values.server.metrics.enabled>: nil pointer evaluating interface {}.enabled
Running the same upgrade with an explicit values file works without issue:
helm upgrade -n my-namespace acme-web-proxy acme/web-proxy \
--version 1.5.0 \
-f helm/acme-web-proxy-values.yaml
Release "acme-web-proxy" has been upgraded. Happy Helming!
Read on to understand why these two commands behave differently and what you can do about it.
The Issue
When upgrading a Helm chart using
--reuse-values, the upgrade fails with a nil pointer error. The error traces back to a template trying to access a values key that does not exist in the stored release values, in this case
.Values.server.metrics.enabled.
The same upgrade succeeds when you pass a values file explicitly using
-f.
The Cause
The difference comes down to how Helm builds the values set that gets rendered into your chart templates.
With
helm upgrade --reuse-values, Helm takes only the user-supplied values stored from the
previous release and uses those as the complete set of overrides. It does
not start from the new chart version’s
values.yaml defaults. Any key introduced in the new chart version is simply missing.
With
helm upgrade -f values.yaml, Helm starts from the new chart’s
values.yaml defaults and merges your file on top. Keys added in the new chart version are populated with their default values before your overrides are applied.
In the example above, chart version
1.5.0 added a new
server.metrics.enabled key. The chart template accesses it directly without a nil guard:
{{- if .Values.server.metrics.enabled }}
# metrics configuration block
{{- end }}
When you upgrade with
--reuse-values, the
server.metrics map does not exist in the stored values at all. Go’s template engine cannot evaluate
.enabled on a nil pointer and the render fails immediately.
This is expected behaviour. The
Helm documentation states that
--reuse-values reuses the last release’s values and merges in any overrides from
--set. Merging in new chart defaults is not part of what it does.
The Fix
There are three approaches depending on your workflow.
Option 1: Always upgrade with an explicit values file
In my opinion, this is the most reliable approach. Keep a values file that captures every override you need and pass it on every upgrade:
helm upgrade -n my-namespace acme-web-proxy acme/web-proxy \
--version 1.5.0 \
-f helm/acme-web-proxy-values.yaml
Helm loads the new chart’s
values.yaml defaults first and then applies your file on top. New keys get their defaults and your existing overrides stay intact.
Option 2: Supply the missing key with –set
If you want to keep using
--reuse-values, you can backfill the missing key on the command line. Check the new chart’s
values.yaml for the expected default and pass it in:
helm upgrade -n my-namespace acme-web-proxy acme/web-proxy \
--version 1.5.0 \
--reuse-values \
--set server.metrics.enabled=false
This resolves the immediate error, but it is a fragile approach for ongoing upgrades. Each time a new chart version introduces a key that a template does not nil-guard, you will hit the same problem again.
Option 3: Use –reset-then-reuse-values (Helm 3.14+)
Helm 3.14 added the
--reset-then-reuse-values flag. It resets to the new chart’s defaults first and then re-applies your previously stored overrides on top:
helm upgrade -n my-namespace acme-web-proxy acme/web-proxy \
--version 1.5.0 \
--reset-then-reuse-values
If you are on Helm 3.14 or later, this flag handles the new defaults problem without requiring you to maintain a full values file. You can check your Helm version with
helm version.
Why –reuse-values is risky across chart version bumps
--reuse-values was designed for cases where you want to re-apply the same set of overrides without listing them again. It works well when upgrading within the same chart version or when a new version does not introduce any new required template values.
Once a chart adds a new key and the template author accesses it without a nil guard such as
{{- if .Values.server.metrics }}, any upgrade using
--reuse-values will break for anyone who does not have that key in their stored values. It is partly a chart authoring problem, but you will encounter it regardless and need to know how to unblock yourself.
The most consistent approach is to treat your values file as the source of truth and always pass
-f on every upgrade. Your intent is explicit, the file is reviewable in source control, and you will not get caught out when a chart adds new keys.
Regards
Follow me on Bluesky
Dean Lewis