Unhandled null value error

I use terraform-plugin-framework to develop a plugin.
I find that when i call func (c Config) Get(ctx context.Context, target interface{}) to convert data from the config. the optional config with Attributes will lead to unhandled null value once the config is not defined, is there any why to avoid this.
here is my code

func (t clusterResourceType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
	return tfsdk.Schema{
		// This description is used by the documentation generator and the language server.
		MarkdownDescription: "Cluster resource",

		Attributes: map[string]tfsdk.Attribute{
			"project_id": {
				MarkdownDescription: "The ID of the project",
				Required:            true,
				Type:                types.StringType,
			},
			"name": {
				MarkdownDescription: "The name of the cluster",
				Required:            true,
				Type:                types.StringType,
			},
			"cluster_id": {
				Computed:            true,
				MarkdownDescription: "The ID of the cluster",
				PlanModifiers: tfsdk.AttributePlanModifiers{
					tfsdk.UseStateForUnknown(),
				},
				Type: types.StringType,
			},
			"cluster_type": {
				MarkdownDescription: "DEVELOPER: create a Developer Tier cluster \n DEDICATED:create a Dedicated Tier cluster",
				Required:            true,
				Type:                types.StringType,
			},
			"cloud_provider": {
				MarkdownDescription: "AWS: the Amazon Web Services cloud provider \n GCP:the Google Cloud Platform cloud provider",
				Required:            true,
				Type:                types.StringType,
			},
			"region": {
				MarkdownDescription: "the region value should match the cloud provider's region code",
				Required:            true,
				Type:                types.StringType,
			},
			"config": {
				MarkdownDescription: "The configuration of the cluster",
				Required:            true,
				Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
					"root_password": {
						Required: true,
						Type:     types.StringType,
					},
					"port": {
						Optional: true,
						Computed: true,
						Type:     types.Int64Type,
						PlanModifiers: tfsdk.AttributePlanModifiers{
							tfsdk.UseStateForUnknown(),
						},
					},
					"components": {
						Optional: true,
						Computed: true,
						PlanModifiers: tfsdk.AttributePlanModifiers{
							tfsdk.UseStateForUnknown()},
						Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
							"tidb": {
								Required: true,
								Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
									"node_size": {
										Required: true,
										Type:     types.StringType,
									},
									"node_quantity": {
										Required: true,
										Type:     types.Int64Type,
									},
								}),
							},
							"tikv": {
								Required: true,
								Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
									"node_size": {
										Required: true,
										Type:     types.StringType,
									},
									"storage_size_gib": {
										Required: true,
										Type:     types.Int64Type,
									},
									"node_quantity": {
										Required: true,
										Type:     types.Int64Type,
									},
								}),
							},
							"tiflash": {
								Optional: true,
								Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{
									"node_size": {
										Required: true,
										Type:     types.StringType,
									},
									"storage_size_gib": {
										Required: true,
										Type:     types.Int64Type,
									},
									"node_quantity": {
										Required: true,
										Type:     types.Int64Type,
									},
								}),
							},
						}),
					},
					"ip_access_list": {
						Optional: true,
						Attributes: tfsdk.ListNestedAttributes(map[string]tfsdk.Attribute{
							"cidr": {
								Required: true,
								Type:     types.StringType,
							},
							"description": {
								Optional: true,
								Type:     types.StringType,
							},
						}),
					},
				}),
			},
		},
	}, nil
}

type clusterResourceData struct {
	ClusterId     types.String  `tfsdk:"cluster_id"`
	ProjectId     types.String  `tfsdk:"project_id"`
	Name          types.String  `tfsdk:"name"`
	ClusterType   types.String  `tfsdk:"cluster_type"`
	CloudProvider types.String  `tfsdk:"cloud_provider"`
	Region        types.String  `tfsdk:"region"`
	Config        ClusterConfig `tfsdk:"config"`
}

type ClusterConfig struct {
	RootPassword string      `tfsdk:"root_password"`
	Port         types.Int64 `tfsdk:"port"`
	Components   Components  `tfsdk:"components"`
	IPAccessList []IPAccess  `tfsdk:"ip_access_list"`
}

type Components struct {
	TiDB    ComponentTiDB    `tfsdk:"tidb"`
	TiKV    ComponentTiKV    `tfsdk:"tikv"`
	TiFlash ComponentTiFlash `tfsdk:"tiflash"`
}

Hi @shiyuhang0 :wave: Welcome to HashiCorp Discuss and thank you for reaching out. Apologies for the confusing error and lack of documentation on this which doesn’t give you enough direction to resolve this issue yourself.

When working with terraform-plugin-framework and trying to get all data from a configuration, plan, or state, the best recommendation at this time is to ensure that all the provider-defined types, such as clusterResourceData in this case, use the framework-defined types for every field, or at the very least, pointer types so null values can be handled.

For example, updating the Config field of clusterResourceData to use the types.Object type in your example code should get the code working better:

type clusterResourceData struct {
	// ... other fields
	Config        types.Object `tfsdk:"config"`
}

You will need to repeat this process for the other fields of the custom types. In the provider logic, you can use the methods on those types types to further convert the nested data into your custom types.

Under the hood what is happening is that terraform-plugin-framework receives data from Terraform that is either a null value, an unknown value, or a known value for each attribute/block. The Get() method on tfsdk.Config, tfsdk.Plan, or tfsdk.State then tries to convert the data it received into the custom type you provided. If the field type cannot properly preserve a null or unknown value, the framework will return the unhandled null value or unhandled unknown value error respectively.

Hope this helps point you in the right direction and thank you again for asking about this.

Thank you for your reply. I have resolved it by using pointer types.
About types.Object, I think it may be a bit complicated once the Object has several levels nested data