Skip to content

Inconsistent marshalling behavior between value and pointer struct literals #367

@sudomateo

Description

@sudomateo

I attempted to create an instance using the following value-based struct literal.

						NetworkInterfaces: oxide.InstanceNetworkInterfaceAttachment{
							Params: []oxide.InstanceNetworkInterfaceCreate{
								{
									Description: fmt.Sprintf("Managed by the Oxide Omni infrastructure provider (%s).", ID),
									Name:        oxide.Name(pctx.GetRequestID()),
									VpcName:     oxide.Name(machineClass.VPC),
									SubnetName:  oxide.Name(machineClass.Subnet),
									IpConfig: oxide.PrivateIpStackCreate{
										Value: oxide.PrivateIpStackCreateV4{
											Value: oxide.PrivateIpv4StackCreate{
												Ip: oxide.Ipv4Assignment{
													Type: oxide.Ipv4AssignmentTypeAuto,
												},
											},
										},
									},
								},
							},
							Type: oxide.InstanceNetworkInterfaceAttachmentTypeCreate,
						},

However, the R18 Omicron API responded with the following error.

failed creating oxide instance: POST https://oxide.sys.rack2.eng.oxide.computer/v1/instances?project=matthewsanabria\n----------- RESPONSE -----------\nStatus: 400 \nMessage: unable to parse JSON body: network_interfaces: unknown variant ``, expected one of `v4`, `v6`, `dual_stack` at line 1 column 802\nRequestID: ad964585-a108-48e1-8bd9-7277a1988c3a\n------- RESPONSE HEADERS -------\nX-Request-Id: [ad964585-a108-48e1-8bd9-7277a1988c3a]\nDate: [Tue, 27 Jan 2026 23:11:42 GMT]\nContent-Length: [203]\nContent-Type: [application/json]\n

This is because the serialized form of this was the following where ip_config.type was empty.

{
  "params": [
    {
      "description": "Managed by the Oxide Omni infrastructure provider (oxide).",
      "ip_config": {
        "type": "",
        "value": {
          "ip": {
            "type": "auto"
          }
        }
      },
      "name": "talos-default-control-planes-grwjt5",
      "subnet_name": "default",
      "vpc_name": "default"
    }
  ],
  "type": "create"
}

Instead, I was supposed to use this pointer-based struct literal.

						NetworkInterfaces: oxide.InstanceNetworkInterfaceAttachment{
							Params: []oxide.InstanceNetworkInterfaceCreate{
								{
									Description: fmt.Sprintf("Managed by the Oxide Omni infrastructure provider (%s).", ID),
									Name:        oxide.Name(pctx.GetRequestID()),
									VpcName:     oxide.Name(machineClass.VPC),
									SubnetName:  oxide.Name(machineClass.Subnet),
									IpConfig: oxide.PrivateIpStackCreate{
										Value: &oxide.PrivateIpStackCreateV4{
											Value: oxide.PrivateIpv4StackCreate{
												Ip: oxide.Ipv4Assignment{
													Type: oxide.Ipv4AssignmentTypeAuto,
												},
											},
										},
									},
								},
							},
							Type: oxide.InstanceNetworkInterfaceAttachmentTypeCreate,
						},

Which serializes to the following. Note how ip_config.type is now correctly v4.

{
  "params": [
    {
      "description": "Managed by the Oxide Omni infrastructure provider (oxide).",
      "ip_config": {
        "type": "v4",
        "value": {
          "ip": {
            "type": "auto"
          }
        }
      },
      "name": "talos-default-control-planes-grwjt5",
      "subnet_name": "default",
      "vpc_name": "default"
    }
  ],
  "type": "create"
}

The diff between the value and pointer JSON is likely clearer.

diff /tmp/value.json /tmp/pointer.json
29c29
<             "type": "",
---
>             "type": "v4",

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions