app-template evaluates values as Go templates — literal {{ }} breaks rendering
Problem
An app on the bjw-s app-template chart suddenly stops deploying. ArgoCD shows the application
Unknown/OutOfSync with a ComparisonError, and every sync for that app is blocked — no
unrelated change to the app can roll out until this is fixed. The underlying Helm error names a
template function that doesn't exist:
error calling tpl: cannot parse template "...": template: gotpl:23: function "provider" not defined
Local helm template / helm lint may pass, which hides the problem.
Observed on readest: a literal {{provider}} token sat inside an initContainer command (a sed
pattern matching a JS i18n string) and silently blocked all readest syncs for ~18 days.
Root Cause
Since app-template 5.2.x, string values — including initContainers.*.command — are passed
through Helm's tpl function (bjw-s.common.values.evaluateTemplate). Any {{ ... }} inside them
is evaluated as a Go template action. A literal double-brace token that isn't valid Go template
syntax (here {{provider}}, a JS i18n interpolation we were trying to match in sed) makes tpl
try to call a function named provider, which is undefined → the whole render fails.
This is a latent bug activated by a chart bump, not a bad edit: the value rendered fine under
app-template 5.1.0 (which did not tpl-evaluate these fields) and broke when Chart.yaml went
5.1.0 → 5.2.1 with no values.yaml change.
Why local rendering can miss it: Chart.lock and charts/ are gitignored, so a workstation may
still hold the older chart on disk while ArgoCD runs helm dependency build and resolves the newer
version from Chart.yaml. See the AGENTS.md pitfall "Forgetting helm dependency build after
updating Chart.yaml".
How to Diagnose
# Render with the SAME chart version ArgoCD resolves — rebuild deps first
helm dependency build apps/<app>/
helm template --include-crds --namespace <ns> <release> apps/<app>/
# A tpl failure names the offending token: function "<token>" not defined
# Find literal double-braces in values that are not meant to be Helm templates
grep -n '{{' apps/<app>/values.yaml
# Confirm what ArgoCD sees
kubectl --context=grigri -n argocd get application <app> \
-o jsonpath='{.status.sync.status} {.status.conditions[*].message}{"\n"}'
Fix / Workaround
- Preferred: don't put literal
{{ }}in values. For readest thesedpatterns were rewritten to match the label text generically (label:[A-Za-z0-9_$]+\("[^"]*",...)) instead of the literalSign in with {{provider}}, removing the braces entirely. This also made the patterns resilient to minified-identifier churn across releases. - If the literal is required, escape it for
tpl: writing{{ "{{provider}}" }}renders to the literal{{provider}}. - After fixing,
helm dependency buildthen re-render to confirm the error is gone, and verify the rendered manifest still contains the intended literal string. Then commit.
Prevention
When embedding shell/sed/regex that must match a literal {{ ... }} (common with JS i18n like
{{provider}}, or templated config snippets) inside an app-template command/args value, escape
it or match around it. Treat any {{ in values.yaml as a Helm template action, because
app-template ≥ 5.2 evaluates it.