Skip to main content
DevOps Insightsguides & answers

Terraform · troubleshooting

Provider produced inconsistent result after apply

After apply the provider returned a different value than it planned, usually because the API rewrote what you sent: its case, order or JSON formatting. On create, Terraform keeps the object but marks it tainted, so the next apply would replace it. Match the value the error shows, upgrade the provider, then untaint.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 9 sections

Before you start

  • The full error text: it names the resource address, the attribute, and the planned and actual values.
  • terraform version, to see the provider version.
  • Access to state commands (terraform state show, terraform untaint) if the object was created.

Run this first

step 1 of 3
terraform apply 2>&1 | grep -B2 -A4 'inconsistent'

Look for .ATTRIBUTE: was …, but now …

The detail line names the attribute and shows the planned and returned values side by side.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

In the error, how do the was and but now values differ?

Cause space

3 of 3 still possible

  • The API rewrites the value you sentCommon
  • A provider release has a bug in that resourceCommon
  • The resource was created, and is now taintedCommon

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    terraform apply 2>&1 | grep -B2 -A4 'inconsistent'

    The detail line names the attribute and shows the planned and returned values side by side.

    Look for .ATTRIBUTE: was …, but now …

  2. Step 2

    terraform state show 'ADDRESS'

    Shows what Terraform saved, which is the value the provider returned.

    Look for The attribute's stored value, and whether the instance is tainted

  3. Step 3

    TF_LOG=WARN terraform apply 2>&1 | grep 'produced an unexpected new value'

    For legacy-SDK resources the same mismatch is only logged; this finds it when something downstream misbehaves.

    Look for but we are tolerating it because it is using the legacy plugin SDK

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

The API rewrites the value you sent

Common

The cloud API stores the value in a canonical form: a different case, a sorted list, reformatted JSON, a default filled in. The provider passes that back unchanged, and it no longer equals the planned value.

Confirm

terraform apply 2>&1 | grep -A3 'unexpected new value'

was X, but now Y where X and Y differ only in case, order, whitespace or formatting

Fix

  • Write the value in the configuration the way the API returns it, the "but now" value: the same case, the same order, or JSON built with jsonencode() rather than a hand-formatted string.
  • Upgrade the provider: normalisation bugs are fixed resource by resource. Check its CHANGELOG for the resource type.
  • If neither helps, report it to the provider with the attribute and both values from the error; the message is right that the provider should plan the value the API returns.

A provider release has a bug in that resource

Common

The error started after a provider upgrade, or the resource type is new in the provider. Recently added resources are the usual place for planned and returned values to disagree.

Confirm

terraform version
git log -p --follow -- .terraform.lock.hcl | grep -E '^[-+] *version' | head

A provider upgrade just before the errors began

Fix

  • Upgrade to the newest provider release with terraform init -upgrade; if that still fails, pin the last version that worked and commit the lock file.
  • Search the provider's issues for the resource type and Provider produced inconsistent result; many have a workaround in the thread.
terraform init -upgrade

Then plan, and read what it proposes for the tainted resource before applying.

The resource was created, and is now tainted

Common

Not a cause but the consequence people trip over: the failed create left the object in state marked tainted. The next apply would destroy it and create it again, and hit the same error if nothing changed.

Confirm

terraform plan | grep -i 'tainted'

is tainted, so must be replaced

Fix

  • First fix the cause (configuration or provider version), so a new plan shows no change to the attribute.
  • If the real object is fine, keep it: terraform untaint ADDRESS. Then plan again and check that it proposes no replacement.
terraform untaint 'ADDRESS'

Only when the created object is correct; the next plan should then show no replacement.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

Terraform plans first and then applies, and it holds the provider to its plan: every value that was known when planning must come back the same. Some APIs quietly rewrite what you send them, for example lowercasing a name, sorting a list or reformatting a JSON policy. When the provider passes that rewritten value back, Terraform sees a broken promise and stops with this error. The resource usually did get created, which is why the next run can surprise you.

Words used on this page

Planned value
What the provider said, during plan, an attribute would be after apply. Known values must not change; only values shown as (known after apply) may.
Tainted
A resource instance Terraform will destroy and recreate on the next apply, because its creation did not finish cleanly.
Legacy plugin SDK
The older library most long-standing provider resources use. For those, Terraform only logs a warning for this inconsistency; the error comes from resources built on the newer plugin framework.
Inconsistent final plan
A related error during apply: once unknown values became known, the provider's updated plan contradicted the original one.
What is actually happening

After apply, Terraform compares the object the provider returned with the plan. Only attributes that were unknown at plan time may differ; any other difference is reported as produced an unexpected new value, with the attribute and both values: .name: was cty.StringVal("MyName"), but now cty.StringVal("myname").

Terraform's own message calls it a provider bug, and strictly it is: the provider should plan the value the API will return. But the trigger is usually a value the API normalises, so you can often avoid it from configuration.

For resources built on the legacy plugin SDK, Terraform tolerates the mismatch and writes a [WARN] line to the log instead. That is why the error mostly appears with newer resources, built on the plugin framework.

What happens to the object: on create, Terraform saves what the provider returned and marks it tainted, so the next plan proposes to destroy and recreate it. On update, the new value is saved as it came back.

Provider produced inconsistent final plan is the same promise broken one step earlier: during apply, after values that were unknown at plan became known, the provider's revised plan changed the action or a value.

How to tell this is your problem
WhereWhat you see
terraform applyError: Provider produced inconsistent result after apply … When applying changes to ADDRESS, provider "registry.terraform.io/hashicorp/aws" produced an unexpected new value: .ATTRIBUTE: was cty.StringVal("…"), but now cty.StringVal("…").
The next terraform planADDRESS is tainted, so must be replaced
terraform apply (related)Error: Provider produced inconsistent final plan … produced an invalid new value for .ATTRIBUTE

How to know it is actually fixed

  • terraform apply completes without the error.
  • The following terraform plan shows no changes, and nothing tainted.

Stopping it happening again

  • Write values in their canonical form from the start: jsonencode() for policies and documents, the case and order the API uses.
  • Upgrade providers in a pull request whose plan and apply you watch, especially when you start using a newly added resource type.

Security considerations

  • Do not hand-edit state to make the values match. Change the configuration or the provider version, and let Terraform record what the API returned.
  • Before running apply again after this error, read the plan: a tainted resource is planned for replacement, and for a database or a key that means data loss.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it