Skip to content

ResponseNewParams fails to unmarshal list of messages as input #557

@kabirkhan

Description

@kabirkhan

Summary

The ResponseNewParams fails to JSON unmarshal of list of messages for the input parameter (instead of just a simple string).

The field-based discriminator union pattern used in internal/apijson/union.go seems to break down in a couple places like this where there isn't a proper discriminator. Happy to contribute a fix if you all don't have time for this.

I'm pretty sure the fix is just to check if the discriminator is not nil here before failing and allowing the logic to fall through to the next steps:

discriminator := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value()
.

I'm not really sure what the repercussions if any would be on the rest of the types though so looking for some guidance.

current

if discriminated && n.Type == gjson.JSON && len(unionEntry.discriminatorKey) != 0 {
	discriminator := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value()
	for _, decoder := range discriminatedDecoders {
		if discriminator == decoder.discriminator {
			inner := v.FieldByIndex(decoder.field.Index)
			return decoder.decoder(n, inner, state)
		}
	}
	return errors.New("apijson: was not able to find discriminated union variant")
}

fix

if discriminated && n.Type == gjson.JSON && len(unionEntry.discriminatorKey) != 0 {
	discriminator := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value()
	if discriminator != nil {
		for _, decoder := range discriminatedDecoders {
			if discriminator == decoder.discriminator {
				inner := v.FieldByIndex(decoder.field.Index)
				return decoder.decoder(n, inner, state)
			}
		}
		return errors.New("apijson: was not able to find discriminated union variant")
	}
}

Minimum reproducible example of the error

This script demonstrates the input unmarshal fails.

package main

import (
	"encoding/json"
	"fmt"

	"github.com/openai/openai-go/v3/responses"
)

func main() {
	inputBytes := `{
	  "model": "gpt-4.1",
	  "type":"message",
	  "input": [
	    {
	      "role": "system",
	      "content": "You are a helpful assistant."
	    },
	    {
	      "role": "user",
	      "content": "What's the square root of 144?"
	    },
	  ],
	  "temperature": 0.7
	}`

	var params responses.ResponseNewParams

	err := json.Unmarshal([]byte(inputBytes), &params)
	if err != nil {
		panic(err)
	}
	paramBytes, _ := json.Marshal(params)

	fmt.Println("Roundtrip", map[string]any{
		"params":     params,
		"paramsJSON": string(paramBytes),
	})

	// paramBytes returns:
	// {"temperature":0.7,"model":"gpt-4.1"}
	// no input list is unmarshalled
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions