cloudposse / cloudposse/atmos

locals breaks stack listing when name_template vars come from parent imports

Open
#2,343 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Go
Stars
1.4k
Forks
175
Avg merge
2d 3h
Merged PRs (30d)
134

Description

### Describe the Bug

## What's broken

`internal/exec/describe_locals.go:deriveStackNameFromTemplate` renders `name_template` using only the current file's `vars:` block — ancestor `_defaults.yaml` imports are NOT merged:

```go
// internal/exec/describe_locals.go (lines ~513–518)
templateData := map[string]any{
"vars": varsSection, // ← current file only, no import merge
}
stackName, err := ProcessTmpl(atmosConfig, "describe-locals-name-template", atmosConfig.Stacks.NameTemplate, templateData, false)
```

In hierarchical `_defaults` layouts (which Atmos docs recommend), identity vars like `namespace`/`tenant`/`stage` live in parent imports. The pre-pass renders `name_template` with those vars missing, producing a malformed name (e.g. `-prod`). The real stack (e.g. `acme-prod`) then disappears from `atmos list stacks`, a
nd `atmos describe component -s acme-prod` fails with "invalid component".

Every existing fixture under `tests/fixtures/scenarios/locals-*` sets identity vars directly in the leaf stack file, so none exercise this path.

## Minimal repro

File tree:

```
repro/
├── atmos.yaml
├── components/terraform/mock/main.tf # empty file
└── stacks/
└── orgs/
├── _defaults.yaml
└── prod.yaml
```

`atmos.yaml`:
```yaml
base_path: "./"
components:
terraform:
base_path: "components/terraform"
stacks:
base_path: "stacks"
included_paths: ["orgs/**/*"]
excluded_paths: ["**/_defaults.yaml"]
name_template: "{{ .vars.namespace }}-{{ .vars.stage }}"
templates:
settings:
enabled: true
```

`stacks/orgs/_defaults.yaml`:
```yaml
vars:
namespace: acme
```

`stacks/orgs/prod.yaml`:
```yaml
import: [./_defaults]
vars:
stage: prod
locals:
foo: bar # any locals block is enough to trigger the bug
components:
terraform:
vpc:
metadata: {component: mock}
vars: {name: "{{ .locals.foo }}"}
```

## Expected vs actual

```
$ atmos list stacks
expected: acme-prod
actual: -prod ← namespace missing, stack unfindable

$ atmos describe component vpc -s acme-prod
expected: describes vpc in acme-prod
actual: Error: invalid component … Could not find the component vpc in the stack acme-prod
```

Remove the `locals:` block and `acme-prod` appears correctly — proving the issue is specific to the locals pre-pass code path.

## Affected versions

Reproduced on v1.216.0. Likely present since file-scoped locals landed (v1.200+).

## Related

- Related to prior work on locals + stack context: #2080, see `docs/fixes/2026-03-15-locals-terraform-state-missing-stack-context.md`.
- Cross-linked sibling issues:
- Performance: N² evaluation of `!terraform.state` in locals during `list stacks` — #2344
- Config: `describe-stacks-name-template` ignores global `ignore_missing_template_values` — #2345

Contributor guide

Open the contributing guide

Research direction

Start in internal/exec/describe_locals.go at deriveStackNameFromTemplate and inspect how imported _defaults.yaml vars are handled before ProcessTmpl runs. Add a fixture under tests/fixtures/scenarios/locals-* based on the namespace and stage repro, then run the relevant locals tests and verify that atmos list stacks shows acme-prod and describe component vpc -s acme-prod succeeds.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, terraform
Domain
cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.