Hmm
, I think I’ll need to see more of a resource schema to really help investigate further. Maybe I can propose one and we can adjust it to get on the same page.
We’ll start simple and then can adjust to more closely reflect the schema you’re working with:
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"configure_me": schema.StringAttribute{
Required: true,
},
"foo": schema.SingleNestedAttribute{
Computed: true,
Attributes: map[string]schema.Attribute{
"bar": schema.StringAttribute{
Computed: true,
},
},
},
},
}
resource "examplecloud_thing" "this" {
configure_me = "hello"
}
output "foo" {
value = examplecloud_thing.this.foo
}
First run
If I have the above schema, the expected behavior would be:
- User declares resource in config (not yet in state) and runs
terraform apply
- Resource plans to create,
foo is unknown (no plan modifiers are on the schema)
Terraform will perform the following actions:
# examplecloud_thing.this will be created
+ resource "examplecloud_thing" "this" {
+ configure_me = "hello"
+ foo = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ foo = (known after apply)
Create method is called, foo is set to a value, saved to state, all good 
Second run
- User changes config, runs
terraform apply
resource "examplecloud_thing" "this" {
configure_me = "hello123"
}
output "foo" {
value = examplecloud_thing.this.foo
}
Read is called, foo has changed, prior state is refreshed with the new value.
- Resource plans to update
configure_me and foo is marked as unknown (no plan modifiers, it’s computed so the value could change)
Terraform will perform the following actions:
# examplecloud_thing.this will be updated in-place
~ resource "examplecloud_thing" "this" {
~ configure_me = "hello" -> "hello123"
~ foo = {
~ bar = "here is a value!" -> (known after apply)
} -> (known after apply)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Changes to Outputs:
~ foo = {
- bar = "here is a value!"
} -> (known after apply)
Update method is called, foo is set to a value, saved to state, all good 
Read influences the prior state that is presented to the plan step, but in the example above, there is no plan modifier to suggest that foo should be populated, so it’s set to unknown.
All of this being said, the error you presented was complaining about the plan saying that .foo was null, which for a Computed value, should only be possible if a planmodifier or default sets it to null. If that’s not happening I think it’s a bug.
The only way I’ve been able to recreate your described error is by adding a default like so:
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"configure_me": schema.StringAttribute{
Required: true,
},
"foo": schema.SingleNestedAttribute{
Computed: true,
Default: objectdefault.StaticValue(types.ObjectNull(map[string]attr.Type{
"bar": types.StringType,
})),
Attributes: map[string]schema.Attribute{
"bar": schema.StringAttribute{
Computed: true,
},
},
},
},
}
Then you’ll get this on your next terraform apply
The Update method is setting the value of foo to something that is not null, which is invalid
Terraform will perform the following actions:
# examplecloud_thing.this will be updated in-place
~ resource "examplecloud_thing" "this" {
- foo = {
- bar = "here is an updated value!" -> null
} -> null
# (1 unchanged attribute hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
Changes to Outputs:
- foo = {
- bar = "here is an updated value!"
} -> null
examplecloud_thing.this: Modifying...
╷
│ Error: Provider produced inconsistent result after apply
│
│ When applying changes to examplecloud_thing.this, provider "provider[\"registry.terraform.io/austinvalle/sandbox\"]" produced an unexpected new value: .foo: was null, but now
│ cty.ObjectVal(map[string]cty.Value{"bar":cty.StringVal("here is an updated value!")}).
│
│ This is a bug in the provider, which should be reported in the provider's own issue tracker.
Apologies, I feel like I’m running us in circles, but hopefully some of this description can help. There is also documentation on how our RPCs map to the functions that you setup in your resources: Framework RPCs | Terraform | HashiCorp Developer