version on a git source
version is registry-only, and deleting it to fix the error leaves the module completely unpinned
module "vpc" {
source = "git::https://github.com/org/tf-vpc.git"
version = "1.2.3"
} Check every module block for the pinning mistakes that let module code change without a change to your configuration. The commonest is version on a git source, where it does not apply.
Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.
The answer appears here
Paste on the left and press Validate. Nothing leaves this tab.
Nothing else to flag.
No formatting problems, and nothing the rules object to. Worth remembering what that covers: this reads the file you pasted, not the account or cluster it will be applied to.
No finding matches that filter.
Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.
version is registry-only, and deleting it to fix the error leaves the module completely unpinned
module "vpc" {
source = "git::https://github.com/org/tf-vpc.git"
version = "1.2.3"
}Terraform clones the default branch, so the module changes whenever somebody merges
module "vpc" {
source = "git::https://github.com/org/tf-vpc.git"
}These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.
version is registry-only. Terraform errors, and the reflex fix is deleting the version line, which leaves the module tracking a branch with no pin at all.
Instead:Pin in the source: append ?ref=v1.2.3 for a tag, or a full commit sha for something that cannot move.
.terraform.lock.hcl records providers only. Two inits weeks apart can fetch different module code with nothing in version control to show it.
Instead:Add a version constraint on every registry module and a ref on every git module. There is no lock file to fall back on.
source is resolved before variables exist, so it must be a literal. The error Terraform gives names variables rather than the source line, which sends people looking in the wrong place.
Instead:Use separate module blocks with count or for_each rather than one dynamic source.
Terraform locks providers. It does not lock modules. Whatever your source address resolves to at init time is what you get, and that is the whole problem.
This is the mistake that looks most like a fix. The version argument is meaningful only when source is a registry address; on a git or http source Terraform rejects it. The dangerous part is the correction: people delete the version line, the configuration is then valid, and the module is completely unpinned. For git the pin belongs in the source address as ?ref=
?ref=main is in the ref position and looks like a version, and it gives you whatever was merged most recently. A tag is better and can still be moved by whoever owns the repository. A full commit sha is the only genuinely immutable option, and it is the right choice when reproducibility matters more than readability.
This is the detail that makes unpinned modules bite. .terraform.lock.hcl records provider versions and hashes, so people reasonably assume modules are covered too. They are not. Two inits weeks apart can fetch different module code with no change to your configuration and nothing in version control to show it.
It has to be a literal string. Variables, locals and data sources do not exist at the point Terraform resolves module sources, so an interpolated source cannot work and the error names variables rather than the source line. If you need different modules per environment, use separate module blocks with count or for_each on each rather than one dynamic source.
Anything else is read as a registry address. So source = "modules/vpc" is not the local directory you meant, it is an attempt to fetch a registry module with a malformed address, and the error is about the registry rather than the path.
Whether the module exists, whether the version constraint resolves to anything, or what the module does. It reads the source addresses in the text you paste, in your browser, and contacts no registry. Run terraform init to confirm the addresses actually resolve.