Terraform Error Guide: 'Unsupported attribute' (reading an attribute that does not exist on an object)
Fix Terraform's 'Unsupported attribute' error: correct typo'd attribute names, use map index syntax, declare module outputs, and match provider versions.
- #terraform
- #troubleshooting
- #errors
- #references
Exact Error Message
Error: Unsupported attribute
on outputs.tf line 3, in output "ip":
3: value = aws_instance.web.public_ipv4
|----------------
| aws_instance.web is object with 40 attributes
This object does not have an attribute named "public_ipv4".
A variant points at a module that does not export the output you read:
Error: Unsupported attribute
on main.tf line 22, in output "vpc_id":
22: value = module.network.id
|----------------
| module.network is object with 3 attributes
This object does not have an attribute named "id". Did you mean "vpc_id"?
What the Error Means
Unsupported attribute is a read error. You used dot access (.attr) to pull a value out of an object — a resource, data source, module, variable, or local — and that object has no attribute by that name. Terraform knows the full shape of the object on the left of the dot, so it lists how many attributes the object actually has and, when it can, suggests the closest match.
This is fundamentally different from Unsupported argument. Unsupported argument is a write error: you tried to assign to an input that a block does not accept (instance_typ = "t3.micro"). Unsupported attribute is a read error: you tried to fetch a value that the object does not expose (aws_instance.web.public_ipv4). One is about setting inputs into a block; the other is about reading outputs off an object.
Like the schema for arguments, the set of readable attributes is tied to the provider version. AWS provider majors rename and remove attributes, so an expression that resolved cleanly under one version can fail under the next with no change to your code.
Common Causes
- Typo’d or wrong attribute name —
aws_instance.web.public_ipv4when the attribute ispublic_ip;public_dnsvspublic_dns_name. - Map key accessed with dot instead of brackets —
local.tags.environmentworks for object attributes, but a map keyed by a string you compute, or a key with characters like-, needslocal.tags["environment"]. - Module output not declared — you read
module.network.idbut the module never defined anoutput "id". You can only read what the module explicitly exports. - Attribute renamed or removed across provider majors — an attribute that existed in AWS provider 4.x was renamed or dropped in 5.x, so the same reference fails after an upgrade.
- Nested attribute is a list and needs an index —
aws_instance.web.network_interface.idfails becausenetwork_interfaceis a list; you needaws_instance.web.network_interface[0].id. - Resource vs data source attribute differences —
data.aws_instanceandresource "aws_instance"do not expose an identical attribute set; copying an expression between them breaks. - Null or optional object attribute — an optional attribute on an
object({...})variable that was never set, or a single-nested attribute that isnull, has no readable sub-attributes.
How to Reproduce the Error
Reference an attribute that does not exist on a resource:
resource "aws_instance" "web" {
ami = "ami-0abcd1234"
instance_type = "t3.micro"
}
output "ip" {
value = aws_instance.web.public_ipv4
}
terraform validate fails immediately: the correct attribute is public_ip. The same shape appears when you read module.network.id but the module only declares output "vpc_id".
Diagnostic Commands
Every command here is read-only. None of them mutate state or infrastructure.
List the real attributes of an existing resource straight from state:
terraform state show aws_instance.web
# aws_instance.web:
resource "aws_instance" "web" {
id = "i-0a1b2c3d4e5f"
ami = "ami-0abcd1234"
instance_type = "t3.micro"
public_ip = "203.0.113.10"
private_ip = "10.0.1.20"
...
}
There is your answer: public_ip, not public_ipv4.
Use the console to probe an attribute interactively before wiring it into config:
echo 'aws_instance.web.public_ip' | terraform console
"203.0.113.10"
For modules, inspect what outputs are actually exported, and grep the module source:
terraform console <<< 'module.network'
grep -rn 'output ' modules/network/
Dump a provider’s full schema to confirm an attribute exists for your installed version:
terraform providers schema -json | jq '.provider_schemas[].resource_schemas["aws_instance"].block.attributes | keys'
Then validate and plan to surface the error with its line number and suggestion:
terraform validate
terraform plan
Avoid terraform apply, terraform destroy, and terraform state rm while diagnosing — you are only inspecting, not changing anything.
Step-by-Step Resolution
- Read the error tail. Terraform tells you the object, how many attributes it has, and often a “Did you mean” suggestion. That suggestion is usually the fix.
- List the true attributes. Run
terraform state show <address>for an existing resource, or check the provider docs for that resource and version. For modules, grep the module’soutputblocks. - Fix a typo. Replace
public_ipv4withpublic_ip,module.network.idwithmodule.network.vpc_id. - Switch dot to brackets for maps. Change
local.tags.environmenttolocal.tags["environment"]when the value is a map rather than an object, or when the key is dynamic. - Declare the missing module output. If you genuinely need
module.network.id, addoutput "id" { value = aws_vpc.this.id }inside the module so it is exported. - Add the list index. Change
...network_interface.idto...network_interface[0].id, or wrap it in aforexpression if you need all of them. - Reconcile provider versions. If the attribute was renamed across a major bump, consult that version’s CHANGELOG and update the reference; pin
versioninrequired_providersso the schema is predictable. - Guard optional attributes. For optional object attributes that may be
null, usetry(var.config.timeout, 30)so a missing attribute degrades to a default instead of erroring. - Re-run
terraform validateuntil it passes, thenterraform planto confirm the value resolves.
Prevention and Best Practices
- Pin provider versions with
~>constraints so the readable attribute set does not shift under you on the nextterraform init -upgrade. - Prefer
terraform consoleandterraform state showover guessing attribute names from memory — they show exactly what the current object exposes. - Use
try()andlookup()for optional or map values that may be absent, instead of raw dot access. - Keep module outputs explicit and well-named; a consumer can only read what you export, so export what consumers need.
- Run
terraform validatein CI on every pull request to catch bad references before they reachplanorapply. - For recurring reference and schema failures across a fleet, an assistant like the incident response workflow can triage the error and point at the offending expression fast.
Related Errors
- Terraform Error Guide: ‘Unsupported argument’ — the write-side counterpart: you assigned to an input a block does not accept.
Unsupported attributeis reading a non-existent attribute via dot access;Unsupported argumentis assigning to a non-existent input argument. - Terraform Error Guide: ‘Reference to undeclared resource’ — when the object on the left of the dot does not exist at all, rather than the attribute on the right.
- Browse more fixes in the Terraform category.
Frequently Asked Questions
What is the difference between “Unsupported attribute” and “Unsupported argument”?
Unsupported attribute happens when you read a value with dot access that the object does not have (aws_instance.web.public_ipv4). Unsupported argument happens when you assign to an input a block does not accept (instance_typ = "t3.micro"). Read versus write.
How do I find the correct attribute name?
Run terraform state show <address> to print every attribute of an existing resource, or echo 'aws_instance.web' | terraform console to inspect the object. The provider documentation for your specific version lists all readable attributes.
Why did this error appear after upgrading my provider? Major provider versions rename and remove attributes. An expression valid under AWS provider 4.x can fail under 5.x. Check that version’s CHANGELOG, update the reference, and pin the provider version to keep the schema stable.
Why does local.tags.environment fail but local.tags["environment"] work?
Dot access works for object attributes whose keys are fixed identifiers. For a map — or any key that is dynamic or contains characters like - — you must use bracket index syntax. When in doubt, use brackets for maps.
Can I read an attribute that a module does not output?
No. A module is a closed box: you can only read attributes it explicitly declares with output blocks. If you need a value, add an output for it inside the module, then reference module.<name>.<output>.
Download the Free 500-Prompt DevOps AI Toolkit
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.