Skip to content

Go session events codegen: flat union type causes breaking changes when adding new event types #1031

Description

@SteveSandersonMS

Problem

The Go codegen for session events (generated_session_events.go) uses quicktype, which represents the discriminated union of all session event Data payloads as a single flat struct with every property from every event type merged together:

type Data struct {
    Content   *string    // from assistant.message
    Title     *string    // from session.title_changed
    ToolName  *string    // from tool.execution_start
    // ... hundreds of fields from all event types
}

This means that if a new session event type is added that has a property with the same name but a different type as an existing event, the shared field must change type to accommodate both. For example, content was *string but a new event used content as a structured object — quicktype changed it to *DataContent (a union of string and map), breaking all existing consumers that read Content as *string.

This is fundamentally unmanageable: any new session event type can silently break the Go SDK's public API by changing the type of a field that existing consumers rely on.

Impact

  • Every go/generated_session_events.go regeneration is a potential breaking change
  • SDK consumers cannot safely use response.Data.Content (or any shared field) without risking that a future codegen run changes its type
  • We had to revert the session events codegen in PR Add modelCapabilities override to all SDK languages #1029 because it introduced a breaking Content type change unrelated to the PR's purpose

Desired outcome

A representation of session event data that is additive-only — adding a new event type should never change the types of existing event payloads. Some options:

  1. Per-event-type structs: Generate a separate struct for each event type's data (e.g., AssistantMessageData, ToolExecutionStartData), with a typed accessor on SessionEvent that returns the concrete type based on the Type discriminator. This is what the C# codegen does.

  2. Embed + type-switch: Keep a minimal base struct with common fields, and generate per-event structs that consumers access via a type switch or typed getter method.

  3. Interface-based: Define a SessionEventData interface with per-type implementations, using a custom UnmarshalJSON that dispatches based on the type field.

All of these ensure that adding event type B never changes the struct shape of event type A.

Current workaround

In #1029 I explicitly reverted the Go-specific session events codegen change, and added a build check suppression (09f7cee). When we fix the code generator, we must also re-enable the build check.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions