Avoiding "Provider produced inconsistent result after apply" error

Hi team.
I want to know if it’s possible to avoid “Provider produced inconsistent result after apply” error when the plan is not equals to the final schema result.

Hi @OrNovo :wave:

Can you provide an example of the Terraform configuration, schema and CRUD functions that you are using that give rise to the error you describe. This will provide more context and allow a more detailed response. Thanks

sure

I will expand on my case -

I’m using an API when every resource have an order field. Because terraform can create resource in parallel, resources may get wrong order on creation, but then it will be fixed when other resources will be created.

e.g. -

resource "coralogix_tco_policy" "tco_policy_1" {
  ....
  order      = 1
}

resource "coralogix_tco_policy" "tco_policy_2" {
  ....
  order = 2
}

Those are the create and read functions -

func (t *TCOPolicyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
	var plan TCOPolicyResourceModel
	diags := req.Plan.Get(ctx, &plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

	createPolicyRequest := extractCreateTcoPolicy(ctx, plan)
	log.Printf("[INFO] Creating new tco-policy: %#v", createPolicyRequest)
	createResp, err := t.client.CreateTCOPolicy(ctx, createPolicyRequest)
	if err != nil {
		log.Printf("[ERROR] Received error: %#v", err)
		resp.Diagnostics.AddError(
			"Error creating tco-policy",
			"Could not create tco-policy, unexpected error: "+err.Error(),
		)
		return
	}
	policy := createResp.GetPolicy()
	log.Printf("[INFO] Submitted new tco-policy: %#v", policy)
	plan.ID = types.StringValue(createResp.GetPolicy().GetId().GetValue())

	plan = flattenTCOPolicy(ctx, policy)

	// Set state to fully populated data
	diags = resp.State.Set(ctx, plan)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
}
func (t *TCOPolicyResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
	var state TCOPolicyResourceModel
	diags := req.State.Get(ctx, &state)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}

//Get refreshed tco-policy value from Coralogix
	id := state.ID.ValueString()
	log.Printf("[INFO] Reading tco-policy: %s", id)
	getPolicyResp, err := t.client.GetTCOPolicy(ctx, &tcopolicies.GetPolicyRequest{Id: wrapperspb.String(id)})
	if err != nil {
		log.Printf("[ERROR] Received error: %#v", err)
		if status.Code(err) == codes.NotFound {
			state.ID = types.StringNull()
			resp.Diagnostics.AddWarning(
				fmt.Sprintf("tco-policy %q is in state, but no longer exists in Coralogix backend", id),
				fmt.Sprintf("%s will be recreated when you apply", id),
			)
		} else {
			resp.Diagnostics.AddError(
				"Error reading tco-policy",
				handleRpcErrorNewFramework(err, "tco-policy"),
			)
		}
		return
	}
	policy := getPolicyResp.GetPolicy()
	log.Printf("[INFO] Received tco-policy: %#v", policy)

	state = flattenTCOPolicy(ctx, policy)
	//
	diags = resp.State.Set(ctx, &state)
	resp.Diagnostics.Append(diags...)
	if resp.Diagnostics.HasError() {
		return
	}
}

After applying this example I’m getting -

│ Error: Provider produced inconsistent result after apply
│ 
│ When applying changes to coralogix_tco_policy.tco_policy_2, provider "provider[\"registry.terraform.io/coralogix/coralogix\"]" produced an unexpected new value: .order: was cty.NumberIntVal(2), but now
│ cty.NumberIntVal(1).

Thanks for the additional context @OrNovo.

If you are supplying a value in Terraform configuration (e.g., order = 1) then this is the value that must be stored in state for that attribute. Terraform will not permit mutation of a value that is defined within the configuration. If you need to store a value that is obtained from an API call, you could add a computed attribute to the schema and populate the attribute with the value that is returned from the API.

@bendbennett but then this field can’t be Required right?

  • If the attribute is Required then it must be supplied in the Terraform configuration.
  • If the attribute is Optional then it can be supplied in the Terraform configuration, but if it is not supplied a null value will be stored for the attribute in state.
  • If the attribute is Computed then it cannot be specified in Terraform configuration but must be specified within the provider.
  • If the attribute is Optional and Computed then it can be specified in the Terraform configuration and if it appears in the configuration then the value must not be mutated by the provider. If the attribute is not specified in the configuration then the provider must set a value for the attribute.

The Handling Data - Schemas section of the Framework documentation has some further information on this area.

1 Like