Paavo Pokkinen
04/21/2026, 9:40 AMPaavo Pokkinen
04/21/2026, 9:40 AMturns out any locals: block triggers atmos to eagerly render imported catalog templates, which fails on .vars.tenant before merge
The catalog template. catalog/gcp/gke/_defaults.yaml:8 has:
name: “{{ .vars.tenant }}-{{ .vars.stage }}-{{ .atmos_component }}”
.vars.tenant is not set in the catalog file itself — it comes from ancestors via orgs/mp/plat/_defaults.yaml (tenant: “plat”).
The two render passes. Atmos normally defers Go-template rendering until after it has merged vars up the import chain. So by the time {{ .vars.tenant }} is evaluated, the parent _defaults chain has populated it.
What locals: changes. When a stack file declares locals:, atmos runs an extra pre-pass: for each file in the import tree, it tries to render templates so it can resolve the locals (and the describe-locals-name-template you saw in debug
logs). In that pre-pass, each file is rendered in isolation — the ancestor _defaults.yaml chain (orgs/mp/_defaults.yaml, plat/_defaults.yaml) has not been merged into .vars yet, because those files aren’t imports of the catalog,
they’re imports of the stack file.
The failure. Go’s text/template runs with missingkey=error by default. When atmos pre-renders catalog/gcp/gke/_defaults.yaml with an empty .vars, .vars.tenant raises map has no entry for key “tenant” and the whole describe stacks call
aborts. Without the locals: block, the pre-pass isn’t triggered and the template is deferred to the merged-vars phase where tenant is populated.
Paavo Pokkinen
04/21/2026, 9:46 AMPaavo Pokkinen
04/21/2026, 10:28 AMPaavo Pokkinen
04/21/2026, 10:54 AMErik Osterman (Cloud Posse)
04/21/2026, 11:30 AMErik Osterman (Cloud Posse)
04/21/2026, 11:30 AMPaavo Pokkinen
04/21/2026, 11:50 AMErik Osterman (Cloud Posse)
04/21/2026, 12:26 PMPaavo Pokkinen
04/21/2026, 12:27 PMAndriy Knysh (Cloud Posse)
04/21/2026, 1:35 PMAndriy Knysh (Cloud Posse)
04/21/2026, 3:56 PM