AWS Builder Center
Terragrunt read one of your two dependency blocks and never mentioned it

Terragrunt read one of your two dependency blocks and never mentioned it

Two dependency blocks sharing a label parsed cleanly in Terragrunt for years, and every reference to that label silently resolved to whichever block came last. No parse warning, no plan diff, nothing in apply. Terragrunt 1.1.4 adds a duplicate-dependency-labels strict control that turns the shadowing into a warning, and into an error on demand. Here is the failure, the flag, and why renaming the label is itself a change worth planning.

Series: Infrastructure-as-code (4 articles)

  1. 2
    Terragrunt read one of your two dependency blocks and never mentioned it This article
Engineer @ DevOps area

The problem

A configuration that declares two VPC dependencies, and reads one of them. It parses. It plans. It applies. Nothing anywhere tells you that half of what you wrote is dead.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# terragrunt.hcl
dependency "vpc" {
config_path = "../vpc-us-east-1"
}

dependency "vpc" {
config_path = "../vpc-us-west-2"
}

inputs = {
# Resolves to ../vpc-us-west-2. Always did.
vpc_id = dependency.vpc.outputs.vpc_id
subnet_ids = dependency.vpc.outputs.private_subnet_ids
}
HCL lets the second block shadow the first. Terragrunt keeps the last one it parsed, and from that point dependency.vpc means us-west-2 everywhere in the file. The us-east-1 unit still gets planned as a dependency and still produces outputs. Those outputs just never reach anything.
Read that config as a reviewer. Two regions are declared, the syntax is valid, and the intent looks obvious. There is no signal that one block is decorative.

Why this class of bug is the expensive one

A crash is a solved problem. Every pipeline catches a failed apply and a validation error, and the failure names the file and the line.
This is the other kind. Valid syntax, confident execution, different meaning. The tool did exactly what it was told, and what it was told was not what anyone meant.
I met the same shape from the other end during a client migration. terraform plan proposed replacing a production database. Nobody had typed destroy. A module swap had changed the resource address, Terraform tracks resources by address rather than by identity, and a changed address reads as delete-then-create:
1
2
3
4
# module.db.aws_db_instance.this will be destroyed
# module.rds.aws_db_instance.this will be created

Plan: 1 to add, 0 to change, 1 to destroy.
The plan was correct. It was also one confirmation away from taking the database. Correct output, wrong meaning, and the only defence was somebody reading the addresses instead of the counts.
Duplicate dependency labels are the same failure mode with a longer fuse.

The flag

Terragrunt 1.1.4, released 27 August 2026, warns when it finds duplicate labels. To make it stop:
1
terragrunt run plan --strict-control duplicate-dependency-labels
1
/path/to/terragrunt.hcl: dependency vpc is declared more than once; every dependency needs an address of its own
The error names the address the blocks are competing for, which is the half that saves you time. The fix is mechanical:
1
2
3
4
5
6
7
8
9
10
11
12
dependency "vpc_use1" {
config_path = "../vpc-us-east-1"
}

dependency "vpc_usw2" {
config_path = "../vpc-us-west-2"
}

inputs = {
primary_vpc_id = dependency.vpc_use1.outputs.vpc_id
dr_vpc_id = dependency.vpc_usw2.outputs.vpc_id
}

The issue that takes a while to appreciate

Renaming the label is not a cleanup. It is a behaviour change.
If a duplicate has been shadowing since 2024, then one of those two blocks has been dead code for two years, and the resources downstream of it have been reading values from the other one. The moment you give both blocks distinct labels and wire the references correctly, the previously-ignored dependency wakes up and starts feeding real values into real resources.
That is a plan you read line by line, not one you approve because the flag told you to rename something.
Important
Before renaming, establish which block was actually winning. That is the one your infrastructure has been built against. The other one describes a state that has never existed.

Wiring it into CI

Strict controls in Terragrunt are a staged deprecation mechanism: a behaviour becomes a warning, then an error under strict mode, then eventually the only behaviour. The documentation recommends enabling strict controls in non-production pipelines so incompatibilities surface while you still choose the timing.
1
2
3
4
5
6
7
# .github/workflows/plan.yml
- name: Terragrunt plan
run: |
terragrunt run plan \
--strict-control duplicate-dependency-labels \
--non-interactive
The order that avoids a bad afternoon:
  1. Add the control to the non-production plan job only.
  2. Run it across every unit once. If nothing fires, the flag costs nothing and stays as a guard.
  3. If it fires, treat each hit as an audit question rather than a rename. Which block was running, and is that the one you wanted?
  4. Promote to the production pipeline once the answers are written down.

Also in 1.1.4

terragrunt scaffold used to write # TODO placeholders for every input, while scaffolding the same component through the Catalog TUI opened a form and collected values properly. The command now opens that same form.
1
2
terragrunt scaffold \
github.com/gruntwork-io/terragrunt-infrastructure-modules-example//modules/mysql
For a module or template it lists the source's variables. For a unit or stack it lists the values.* references the configuration makes, which are written to terragrunt.values.hcl. Dismissing with esc writes nothing.
ContextBehaviour
Interactive terminalOpens the form
--non-interactivePlaceholders, exactly as before
stdin is not a terminalPlaceholders, exactly as before
Source declares no inputsSkipped
The CI-safety design is the part worth noting. A scaffold running inside a pipeline, or driven by another program, behaves exactly as it did before the upgrade.

Honest trade-offs

The strict control finds duplicate labels in one file. It does not find the broader version of this problem, which is a reference that resolves to something other than what its name suggests. A dependency named vpc pointing at ../vpc-staging inside a production unit parses fine, is unique, and is still wrong.
The flag also does nothing about dependencies that are declared and genuinely unused. Those are harmless and noisy, and no tooling currently separates them from the dangerous kind.
And enabling the control on a large repository will produce a list, not a green check. Budget the time before you add it to a pipeline other people depend on.

Summary

Two dependency blocks with the same label were readable, valid, and wrong for as long as Terragrunt has had dependencies. As of 1.1.4 there is a warning by default and a strict control that turns it into an error.
Add the flag in non-production now, while a hit is an audit item rather than a broken pipeline. If it fires, find out which block has been running before you rename anything, because that answer is what your infrastructure is actually built on.
A crash tells you where it broke. A silent misread lets you ship it.

Series: Infrastructure-as-code (4 articles)

  1. 2
    Terragrunt read one of your two dependency blocks and never mentioned it This article
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article