Skip to content

Union type deserialization bug: response_format variants incorrectly parsed as OfText #568

@JulieLiu99

Description

@JulieLiu99

Bug Description

The OpenAI Go SDK incorrectly deserializes all response_format types as OfText, causing json_object and json_schema formats to lose their schema data.

Impact

  • Structured output (json_object, json_schema) completely broken when deserializing from JSON
  • Any application loading chat completion parameters from JSON loses schema functionality
  • Applications storing/forwarding OpenAI requests cannot use structured output
  • Silent data loss - no errors thrown, just wrong behavior

Reproduction

package main

import (
    "encoding/json"
    "fmt"

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

func main() {
    // Test broken response_format types
    tests := []struct {
        name     string
        json     string
        expected string
    }{
        {"json_object", `{"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}], "response_format": {"type": "json_object"}}`, "OfJSONObject"},
        {"json_schema", `{"model": "gpt-4", "messages": [{"role": "user", "content": "Hello"}], "response_format": {"type": "json_schema", "json_schema": {"name": "test", "schema": {"type": "object", "properties": {"answer": {"type": "string"}}}}}}`, "OfJSONSchema"},
    }

    fmt.Println("JSON → SDK deserialization:")

    for _, test := range tests {
        var params openai.ChatCompletionNewParams
        if err := json.Unmarshal([]byte(test.json), &params); err != nil {
            fmt.Println("unmarshal err:", err)
            continue
        }

        actual := "none"
        if params.ResponseFormat.OfText != nil {
            actual = "OfText"
        } else if params.ResponseFormat.OfJSONObject != nil {
            actual = "OfJSONObject"
        } else if params.ResponseFormat.OfJSONSchema != nil {
            actual = "OfJSONSchema"
        }

        fmt.Printf("  FAIL %s: expected %s, got %s\n", test.name, test.expected, actual)
    }

    // Round-trip test
    fmt.Println("\nRound-trip test:")
    input := `{"type": "json_schema", "json_schema": {"name": "test", "schema": {"type": "object"}}}`

    var format openai.ChatCompletionNewParamsResponseFormatUnion
    json.Unmarshal([]byte(input), &format)
    output, _ := json.Marshal(format)

    fmt.Printf("  Input:  %s\n", input)
    fmt.Printf("  Output: %s\n", string(output))
    fmt.Println("  FAIL Data corrupted - schema information lost")
}

Test Output

JSON → SDK deserialization:
  FAIL json_object: expected OfJSONObject, got OfText
  FAIL json_schema: expected OfJSONSchema, got OfText

Round-trip test:
  Input:  {"type": "json_schema", "json_schema": {"name": "test", "schema": {"type": "object"}}}
  Output: {"type":"json_schema"}
  FAIL Data corrupted - schema information lost

Environment

  • Go version: go1.25.3 darwin/arm64
  • openai-go version: v1.12.0
  • OS: macOS (Darwin 24.6.0)

Root Cause

Union type deserialization logic appears to default to OfText regardless of actual JSON content.

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