TL;DR
Run terraform init in your working directory before running terraform plan or terraform apply. That's it.
terraform init
terraform plan
Already ran init in CI/CD but still hitting this? Jump to the deeper causes section below.
The Error
Error: Module not installed
This module is not yet installed. Run "terraform init" to install all modules required by this configuration.
Terraform throws this when it finds a module block in your .tf files but can't locate the downloaded source under .terraform/modules/. The module metadata isn't there yet โ so Terraform stops before doing anything else.
Root Cause
Say you have a module block like this:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.2"
name = "my-vpc"
cidr = "10.0.0.0/16"
}
Before Terraform can evaluate that block, it needs the actual module source on disk. The terraform init command handles that fetch. It writes to two places:
.terraform/modules/โ the downloaded module files.terraform/modules/modules.jsonโ registry metadata
No .terraform/modules/ directory? Terraform bails immediately. This happens when plan runs before init, or when someone deleted .terraform/ without reinitializing.
The most common triggers:
- Fresh repo clone โ
.terraform/is gitignored (correctly), so it's never committed - CI/CD pipeline where the
initstep was skipped or failed silently - New
moduleblock added butinitwasn't re-run - Someone ran
rm -rf .terraform/to clean up and forgot to reinitialize - Branch switch where a different module source or version is declared
Fix Approaches
Approach 1 โ Standard fix (local development)
# Navigate to the Terraform working directory
cd path/to/your/terraform
# Initialize โ downloads modules and providers
terraform init
# Now plan works
terraform plan
Approach 2 โ Added a new module to existing config
Added a new module block to an already-initialized workspace? Re-run init to pull in the new source:
terraform init -upgrade
The -upgrade flag isn't strictly required (plain init works fine), but it's a good habit โ it also bumps providers to their latest allowed versions within your declared constraints.
Approach 3 โ CI/CD pipelines
In GitHub Actions, GitLab CI, or similar, keep init and plan as explicit separate steps:
# GitHub Actions example
- name: Terraform Init
run: terraform init
working-directory: ./infra
- name: Terraform Plan
run: terraform plan
working-directory: ./infra
Running a remote backend (S3, GCS, Terraform Cloud)? Pass backend config explicitly โ otherwise init can fail silently on missing credentials and your pipeline moves on anyway:
terraform init \
-backend-config="bucket=my-tf-state" \
-backend-config="key=prod/terraform.tfstate" \
-backend-config="region=us-east-1"
Approach 4 โ Module from local path not found
Local path modules work differently than registry ones. Check the declaration:
module "networking" {
source = "../modules/networking"
}
That path must exist relative to the calling module. A typo here causes init to fail โ and a failed init means plan throws this same error. Verify before re-running:
# Confirm the path resolves
ls ../modules/networking
# Then reinitialize
terraform init
Approach 5 โ Working directory mismatch
This one catches people in monorepos. Running init and plan from different directories is the culprit โ each directory needs its own .terraform/:
# Wrong โ init ran in /infra, plan runs from /
cd /infra && terraform init
cd / && terraform plan # โ fails, no .terraform here
# Correct โ both commands in the same directory
cd /infra
terraform init
terraform plan
Verification
Once init finishes, confirm the modules landed correctly:
# Check the modules directory exists and has content
ls .terraform/modules/
# Should show modules.json plus a directory for each module
# Example output:
# modules.json vpc/
Run terraform plan. It should sail past the module loading stage and print either a resource diff or the familiar "No changes" message:
terraform plan
# Successful output starts with:
# Terraform used the selected providers to generate the following execution plan...
Prevention Tips
- Keep
.terraform/in.gitignoreโ it's in Terraform's official template, but older repos sometimes have it missing. Worth a quick check. - Add a one-liner to your repo README: "Run
terraform initafter cloning." Saves every new contributor from hitting this. - In CI/CD, treat
initandplanas separate named steps. When something breaks, you'll know immediately which step failed. - Working with deeply nested modules and a messy
.terraform.lock.hcl? Paste the lock file content into ToolCraft's YAML โ JSON converter to quickly spot structural issues โ HCL's key-value syntax is close enough that a converter flags mismatched brackets and malformed blocks that are easy to miss by eye.
Quick Reference
# Always run init first
terraform init
# Re-run init after adding or changing modules
terraform init
# Upgrade providers and modules to latest allowed versions
terraform init -upgrade
# Verify modules are installed
ls .terraform/modules/
cat .terraform/modules/modules.json

