Terraform Error Guide: 'Reference to undeclared module' — Declare or Rename the Module Block and Re-init
Fix Terraform 'Reference to undeclared module': declare the missing module block, match the name in your module.<name> references, then run terraform init.
- #terraform
- #iac
- #troubleshooting
- #errors
Stuck on this Terraform error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
Terraform raises Reference to undeclared module during terraform validate or terraform plan when your configuration reads an output or attribute from a module — using the module.<name> syntax — but no module "<name>" block with that exact label exists in the current module. Terraform builds its reference graph from the module blocks it can see in the working directory’s .tf files; if you reference module.network.vpc_id but the block is actually labelled module "networking" (or was never added, or lives in a different directory), the name resolves to nothing and Terraform aborts before touching any provider.
The literal message looks like this:
Error: Reference to undeclared module
on main.tf line 24, in resource "aws_instance" "app":
24: subnet_id = module.network.private_subnet_ids[0]
No module call named "network" is declared in the root module.
The key phrase is “No module call named … is declared”. Terraform is telling you the label after module. does not match any module "<label>" block in the same module scope. This is a static configuration error, not a runtime or state error — nothing has been applied and no credentials are involved.
Symptoms
terraform planorterraform validatefails immediately withReference to undeclared moduleand never reaches the “Refreshing state…” phase.- The error cites a specific file and line where
module.<name>.<attribute>is used. - The message ends with
No module call named "<name>" is declared in the root module(or in a named child module). - The configuration references a module you believe exists — often because a
moduleblock was renamed, moved to another directory, commented out, or lost in a merge. terraform initmay succeed (if the source is still valid) yetplanstill fails, because init downloads sources while plan resolves references.
Common Root Causes
- Label mismatch: the reference uses
module.networkbut the block is labelledmodule "networking". The label aftermodulein the block and the segment aftermodule.in the reference must match character-for-character. - Module block never declared: you copied a reference from another project or wrote it ahead of the block, but never added the corresponding
module "<name>" { source = ... }. - Wrong scope: the
moduleblock is declared in the root module, but the reference lives inside a child module (or vice versa). Modules do not inherit each other’s calls; each.tfscope sees only its ownmoduleblocks. - Commented-out or deleted block: a refactor commented out or removed the
moduleblock but left the references behind. - Missing
terraform initafter adding the block: if the block exists but itssourcewas never initialised,initerrors first; after fixing that, the reference resolves. - File not in the working directory: the
moduleblock lives in a file that is excluded (e.g.,.tf.jsonmisnamed, or file in a subfolder that Terraform does not load — Terraform only reads.tf/.tf.jsonin the current directory, not recursively).
Diagnostic Workflow
Start by validating so Terraform points at the exact offending reference:
terraform validate
terraform fmt -check
List every module call Terraform actually sees, and grep your configuration for the label you expect:
# Every "module" block label in the current directory
grep -rn 'module "' *.tf
# Every reference to that module name
grep -rn 'module\.network' *.tf
If the two lists disagree, you have found the mismatch. Here is a configuration that produces the error — the reference and the block label do not match:
# main.tf — references module.network...
resource "aws_instance" "app" {
ami = "ami-0abcd1234"
instance_type = "t3.micro"
subnet_id = module.network.private_subnet_ids[0] # <-- "network"
}
# network.tf — ...but the block is labelled "networking"
module "networking" { # <-- "networking"
source = "./modules/vpc"
cidr_block = "10.0.0.0/16"
}
Because no block is labelled network, module.network.private_subnet_ids is undeclared. Confirm the module’s real outputs to be sure the attribute exists too:
terraform providers
terraform console <<'EOF'
module.networking
EOF
Example Root Cause Analysis
A team renamed their VPC module block from module "network" to module "networking" to match a new naming convention, updating network.tf but missing three references in main.tf and security.tf. On the next terraform plan, CI failed with:
Error: Reference to undeclared module
on main.tf line 24, in resource "aws_instance" "app":
24: subnet_id = module.network.private_subnet_ids[0]
No module call named "network" is declared in the root module.
Running grep -rn 'module "' *.tf returned only module "networking", while grep -rn 'module\.network' *.tf returned three hits — proving the references still pointed at the old label. The fix was to update every module.network.* reference to module.networking.*, then re-run terraform init (the module’s local source path had also moved) and terraform validate. Plan succeeded on the next run. The root cause was an incomplete rename: the block label changed but the references did not, and there was no terraform validate gate in the pre-commit hook to catch it locally.
Prevention Best Practices
- Run
terraform validatein a pre-commit hook and in CI so undeclared references are caught before merge, not at apply time. - When renaming a
moduleblock label,grep -rn 'module\.<oldname>'across the repo and update every reference in the same commit. - Keep each module’s
moduleblocks and their references in the same working directory; remember Terraform loads.tffiles only from the current directory, non-recursively. - Use
terraform fmtand a consistent naming convention (e.g., match the block label to the module’s purpose) to reduce accidental drift between block and reference. - After adding or moving any
moduleblock, runterraform initbeforeplanso new sources are installed and references resolve. - Review module outputs with
terraform consoleor the module’soutputs.tfso you reference attributes that actually exist.
Quick Command Reference
# Show the exact undeclared reference
terraform validate
# Find declared module labels vs. references
grep -rn 'module "' *.tf
grep -rn 'module\.network' *.tf
# Re-install module sources after adding/moving a block
terraform init
terraform init -upgrade
# Inspect a module's real outputs
terraform console
# Re-plan once references and labels agree
terraform plan
Conclusion
Reference to undeclared module is Terraform telling you that a module.<name> reference has no matching module "<name>" block in the same scope. It is almost always a label mismatch, a missing block, or a wrong-scope reference — never a provider or state problem. Reconcile the block labels with their references (a quick grep makes the mismatch obvious), run terraform init if you added or moved a source, and confirm with terraform validate. Adding a validate gate to your pre-commit hooks and CI stops this error from ever reaching a plan again.
Fixed it? Get 500 Terraform & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Stuck on this? Start guided troubleshooting
Open an interactive diagnostic session with this error already loaded. Work a step-by-step plan, record what each check returns, land on a root cause, and export a clean incident summary — no account needed to start.
Did this fix your issue?
Solved it a different way?
Share the fix that worked for you — reviewed, then published to help the next engineer.
That looks like it may contain a secret (key, token, password, or connection string). Please remove it — a note with a detected secret can’t be published.
Thanks — that helps. Published notes appear after a quick review.
Trending errors this week
The error guides other engineers are actually reading right now.
- 1mount: wrong fs type, bad option, bad superblock
- 2Docker 'failed to set up container networking': Fix the Bridge and IP Pool
- 3Docker 'failed to create shim task': How to Fix the containerd Runtime Error
- 4Transport endpoint is not connected
- 5modprobe: FATAL: Module not found
- 6mount: wrong fs type, bad option, bad superblock
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.