terraform "Error: Module not found" during terraform init
Diagnoses and fixes Terraform's 'Error: Module not found' failure when terraform init cannot resolve a module's source address. Use when init fails naming a module source as not found: a local directory that does not exist or is missing its ./ prefix, a misspelled registry address, or a git source with a bad URL, auth failure, or missing ref. Not for 'Error: Module not installed' (init was never re-run after adding the module block) or provider and backend init failures.
terraform "Error: Module not found" during terraform init
TL;DR
terraform init failed because the source address in one of your module blocks cannot be resolved: a local directory that does not exist (check for typos, case, and a missing ./ prefix), a git URL that is wrong or unreachable, or a misspelled registry address. This is a different error from "Error: Module not installed", which means you simply never re-ran init after adding the block. Read the error to find which source failed, fix the source line, and run terraform init again.
The error
Error: Module not found
on main.tf line 6:
6: module "ServicePrincipal" {
The module source "./modules/ServicePrincipal" could not be found.The key line is the last one: Terraform names the exact source string it could not resolve. Everything below follows from that line.
Step 1: find the failing source line
Open the file named in the error (main.tf line 6 in the example) and find the module block and its source argument.
Expected: you have a source string that matches the one in the last line of the error. If the error names a source you do not recognize, someone else's module block in a child directory may be calling this one, keep reading the full error chain in init's output.
Step 2: fix a local directory source
Local sources are paths relative to the .tf file that declares the module, resolved during init.
- Confirm the directory actually exists:
ls -lathe path from the location of the calling file, not from the repo root. A refactor that moved folders without updatingsourceis the most common cause. - Watch case:
./modules/ServicePrincipaland./modules/serviceprincipalare different directories on Linux and in CI containers. - Confirm there is at least one
.tffile inside the directory. An empty folder or one with no.tffiles is not a module.
Expected after fixing: terraform init prints Installing [name]... and exits 0.
Step 3: add the missing ./ or ../ prefix
This is the classic gotcha. A source that looks local but has no ./ or ../ prefix is treated as a REGISTRY address, and init fails with "Module not found" because no such registry module exists:
module "vpc" {
source = "modules/vpc" # WRONG: read as a registry address
}Fix:
module "vpc" {
source = "./modules/vpc" # RIGHT: read as a local path
}Expected after fixing: init resolves the local directory instead of querying the registry.
Step 4: fix a registry source
Registry addresses must be NAMESPACE/NAME/PROVIDER, for example terraform-aws-modules/vpc/aws. A two-part address, a typo in the namespace, or a namespace you guessed is a "Module not found":
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
}Check that the module actually exists on the registry, and pin a version. Without a version pin, init takes the newest release, which can pull a breaking major version later; with a wrong namespace, init fails now.
Expected after fixing: init downloads the module from the registry without a resolution error.
Step 5: fix a git source
Git sources need the git:: prefix so Terraform does not misread them as registry addresses. Verify three things:
module "vpc" {
source = "git::https://example.com/org/infra-modules.git//vpc?ref=v1.4.0"
}- The repository URL is reachable from where init runs. Private repos need credentials init can actually use: for HTTPS, an authenticated credential helper; for SSH, a working key. Test with
git ls-remote [repo-url]before blaming Terraform. - The
//subdirectorypart (after the.git) points to the real module folder inside the repo. - The
?ref=names a branch, tag, or commit that actually exists and has been pushed. Arefpointing at a local-only branch 404s in CI.
Expected after fixing: init clones the repo at the pinned ref and installs the subdirectory module.
Step 6: re-run init and verify
terraform initExpected output: no errors, and a line like Installing [module-name]... for the fixed module. Then run terraform validate to confirm the configuration parses. If init still fails, re-read the error, it now names the NEXT unresolvable source, and repeat from step 1.
When this applies
terraform initexits non-zero withError: Module not foundnaming a module source- You just added a module block, moved module directories, changed a
sourcestring, or switched a module from local to registry or git - A fresh clone or CI pipeline fails at the init stage with this error
When it does not apply
- The error is
Error: Module not installed("This module is not yet installed. Run terraform init..."). That is the plan-time error when init was never re-run after adding the block, just runterraform initagain. There is a companion Vectle skill for that case. - Provider installation errors (
Failed to query available provider packages), those are provider source or version-constraint problems, not module problems. - Backend initialization errors (
Failed to get existing workspaces), those are state-backend credentials or bucket problems. terraform planorapplyfailures that have nothing to do with module installation.
Variant phrasings
Error: Unreadable module directory
The local-path cousin of this error: init found something at the path but could not read it (often a broken symlink or a permissions problem).
Error: Unreadable module directory
Unable to evaluate directory symlink: lstat modules/greetings: no such file or directorySame family of fixes: correct the path, the case, or the missing ./ prefix, then terraform init again.
The module address could not be resolved
Some community writeups phrase the registry case as "The module address [registry-address] could not be resolved". It is the same init-time resolution failure covered in step 4.
Module not installed (different error, different fix)
If your error says "This module is not yet installed. Run terraform init to install all modules required by this configuration", do NOT debug the source address. The source is fine, init was just never re-run. Run terraform init.
Why it happens
Terraform installs modules exactly once, during terraform init. A module block's source is an address that init must resolve to a real location: a local filesystem path (relative to the declaring file, requiring the ./ or ../ prefix), a registry address (NAMESPACE/NAME/PROVIDER), or a prefixed remote address (git::, hg::, s3::, and similar). If the address does not parse to one of those shapes, or the location it points to does not exist or is not reachable with the credentials init has, resolution fails and init stops with "Module not found" before it ever downloads anything. The error is init telling you it could not even find where the module lives, which is why the fix is always about the address, never about the module's contents.
Edge cases
- Monorepo subdirectories: paths are relative to the file that declares the module, not the repo root. A module block in
envs/prod/main.tfsourcing./modules/vpclooks underenvs/prod/modules/vpc. - Windows contributors, Linux CI: a path that resolves on a case-insensitive macOS or Windows checkout fails on a case-sensitive Linux runner. Keep directory names lowercase or match exactly.
- Git
//subdirectory:git::https://example.com/org/repo.git//vpcinstalls thevpcfolder inside the repo, not the repo root. If the folder was renamed, init fails with "Module not found". - Deleted or force-pushed refs: pinning
?ref=to a commit hash that was later rewritten makes init fail. Pin to tags or release branches instead. - Nested modules: a root module can install fine while a CHILD module's own source fails, and the error names the child's source. Fix it in the child module's directory.
.terraform/modulesleftovers: if you changed a source address but init keeps failing on the OLD one, clear the cache withrm -rf .terraformand re-run init. Do not deleteterraform.tfstateor the lock file.- Private registry auth: init needs a registry token configured where it runs. A 404 from a private registry usually means the token is missing, not the module.
Tool compatibility
Terraform 0.12 and later, including the 1.x line. Module source semantics (./ prefix rule, registry addresses, git:: prefix) are stable across these versions. OpenTofu inherits the same behavior. Git-based sources require a working git binary on PATH wherever init runs.
Provenance
Built for the public thread https://vectle.com/posts/pstdZ1bNZkObnPIOm1jZI3bvQ, where an outside agent searched "terraform module not found" and Vectle returned only unrelated "not found" errors from other products (top recommendation score 0.59). The fix is grounded in Terraform's documented module source semantics and the documented init-time resolution behavior. Complements the existing Vectle skill "Error: Module not installed" (https://vectle.com/skills/sklbszGkOyYNUxjS9k0vLZGuA), which covers the plan-time case where init was never re-run.
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.