What happened?
After bumping our GCP provider (a fork of provider-upjet-gcp) to terraform-provider-google-beta 7.43, every google_container_node_pool create that doesn't set kubelet_config fails with:
async create failed: recovered from panic: value is null
The stack trace ends in the terraform provider:
github.com/hashicorp/go-cty/cty.Value.LengthInt
go-cty@v1.5.0/cty/value_ops.go:997
github.com/hashicorp/terraform-provider-google-beta/google-beta/services/container.expandNodeConfig
google-beta/services/container/node_config.go:1912
github.com/hashicorp/terraform-provider-google-beta/google-beta/services/container.expandNodePool
That line is hashicorp's "cpu_cfs_quota fix", present since roughly google-beta 7.6-7.10: it reads d.GetRawConfig() and calls LengthInt() on node_config[0].kubelet_config without a null check.
This looks like the remaining gap after #319 and #685: those made sure RawConfig/RawPlan are always populated, using the value built by schema.JSONMapToStateValue(params, schemaBlock) (pkg/controller/external_tfpluginsdk.go:267). The problem is the shape of that value: with ctyjson decoding, any block missing from params comes out as null. Terraform core never hands providers that shape — unset list/set blocks decode to empty collections — so provider code feels free to call LengthInt()/Index() on them without IsNull() checks, and hashicorp has no reason to accept that as a bug since it's unreachable through terraform itself. These "was it actually set in config" checks via GetRawConfig are getting more common in the hashicorp providers, so I'd expect more of these over time.
FWIW provider-upjet-gcp main is affected too: it pins terraform-provider-google e40b7568, which has the same unguarded code (it's also in the v7.39.0 the Makefile references), so NodePool creates without kubeletConfig should panic there as well, on both the google and google-beta flavors.
How can we reproduce it?
On a provider built against terraform-provider-google(-beta) >= ~7.10, create a NodePool MR with nodeConfig set but no nodeConfig.kubeletConfig. Every reconcile panics as above. Adding kubeletConfig to the spec makes the same create go through, which confirms where it comes from.
Possible fix
Make the synthesized RawConfig match what Terraform core produces: empty lists/sets instead of null for unset block attributes, in the same place #685 already normalizes things. That would fix this whole class of panic for every provider rather than chasing individual guards in provider forks. Happy to send a PR if that direction sounds right.
Environment
- upjet
v2.4.1-0.20260728103920-4f6e6e10dff2
- terraform-plugin-sdk
v2.40.1
- terraform-provider-google-beta 7.43 (
c1a4c311)
- crossplane-runtime v2.3.3
What happened?
After bumping our GCP provider (a fork of provider-upjet-gcp) to terraform-provider-google-beta 7.43, every
google_container_node_poolcreate that doesn't setkubelet_configfails with:The stack trace ends in the terraform provider:
That line is hashicorp's "cpu_cfs_quota fix", present since roughly google-beta 7.6-7.10: it reads
d.GetRawConfig()and callsLengthInt()onnode_config[0].kubelet_configwithout a null check.This looks like the remaining gap after #319 and #685: those made sure RawConfig/RawPlan are always populated, using the value built by
schema.JSONMapToStateValue(params, schemaBlock)(pkg/controller/external_tfpluginsdk.go:267). The problem is the shape of that value: with ctyjson decoding, any block missing from params comes out as null. Terraform core never hands providers that shape — unset list/set blocks decode to empty collections — so provider code feels free to callLengthInt()/Index()on them withoutIsNull()checks, and hashicorp has no reason to accept that as a bug since it's unreachable through terraform itself. These "was it actually set in config" checks via GetRawConfig are getting more common in the hashicorp providers, so I'd expect more of these over time.FWIW provider-upjet-gcp main is affected too: it pins terraform-provider-google
e40b7568, which has the same unguarded code (it's also in the v7.39.0 the Makefile references), so NodePool creates withoutkubeletConfigshould panic there as well, on both the google and google-beta flavors.How can we reproduce it?
On a provider built against terraform-provider-google(-beta) >= ~7.10, create a NodePool MR with
nodeConfigset but nonodeConfig.kubeletConfig. Every reconcile panics as above. AddingkubeletConfigto the spec makes the same create go through, which confirms where it comes from.Possible fix
Make the synthesized RawConfig match what Terraform core produces: empty lists/sets instead of null for unset block attributes, in the same place #685 already normalizes things. That would fix this whole class of panic for every provider rather than chasing individual guards in provider forks. Happy to send a PR if that direction sounds right.
Environment
v2.4.1-0.20260728103920-4f6e6e10dff2v2.40.1c1a4c311)