Skip to content

fix(go): nullable additionalProperties produces wrong map value type#268

Closed
PatrickKoss wants to merge 4 commits intostackitcloud:mainfrom
PatrickKoss:fix/nullable-additional-properties
Closed

fix(go): nullable additionalProperties produces wrong map value type#268
PatrickKoss wants to merge 4 commits intostackitcloud:mainfrom
PatrickKoss:fix/nullable-additional-properties

Conversation

@PatrickKoss
Copy link
Copy Markdown

Problem

When an OpenAPI schema has additionalProperties with nullable: true on the value type, the Go generator emits map[string]string instead of map[string]*string — dropping the nullability of the map value. This makes it impossible for SDK consumers to send null values, which is the documented signal in many of our APIs to "delete this entry."

Example from the mlflow API:

LabelPatch:
  type: object
  additionalProperties:
    type: string
    nullable: true

Generated today:

Labels *map[string]string `json:"labels,omitempty"`

Expected:

Labels *map[string]*string `json:"labels,omitempty"`

Root cause

This is an upstream limitation in AbstractGoCodegen.getTypeDeclaration (verified by decompiling the generator JAR):

  • For arrays, the inner schema's nullable: true flag triggers a * prefix on the item type — []*string is produced correctly.
  • For maps, the inner schema is read but its nullable flag is ignoredmap[string]string is always produced.

We can't patch upstream Java codegen from this repo, so the fix lives in our Mustache templates.

Fix

Added a small reusable Mustache partial field_type.mustache that returns the corrected type when the property is a map with a nullable value, and the upstream-computed dataType otherwise:

{{#isMap}}{{#additionalProperties.isNullable}}map[string]*{{{additionalProperties.dataType}}}{{/additionalProperties.isNullable}}{{^additionalProperties.isNullable}}{{{dataType}}}{{/additionalProperties.isNullable}}{{/isMap}}{{^isMap}}{{{dataType}}}{{/isMap}}

Applied to both generation paths:

  • New path (languages/golang/templates/, OpenAPI generator v7.22.0) — copied the upstream model_simple.mustache into the repo and replaced {{{dataType}}} / {{{vendorExtensions.x-go-base-type}}} with {{>field_type}} at the struct field, both getters, *Ok returns, setter, doc comment, and WithoutEmbeddedStruct block.
  • Compat-layer path (languages/golang/compat-layer/templates/, OpenAPI generator v6.6.0) — patched the three {{dataType}} sites in the {{#isContainer}}{{^isFreeFormObject}}{{^isArray}} block (AttributeType / ArgType / RetType aliases).

The discriminator is additionalProperties.isNullable. Schemas without nullable: true on the inner type are unaffected — the partial collapses to {{{dataType}}} and the existing rendering is preserved unchanged.

Files changed

  • New languages/golang/templates/field_type.mustache
  • New languages/golang/templates/model_simple.mustache
  • New languages/golang/compat-layer/templates/field_type.mustache
  • Modified languages/golang/compat-layer/templates/model_simple.mustache (3 lines)

Verification

Generated against the mlflow API spec (which has both nullable and non-nullable additionalProperties schemas) and confirmed:

Schemas using LabelPatch (nullable additionalProperties) — every reference is now consistent at *map[string]*string:

// PartialUpdateInstancePayload, PartialUpdateTokenPayload
Labels *map[string]*string `json:"labels,omitempty"`
func (o *PartialUpdateInstancePayload) GetLabels() map[string]*string { … }
func (o *PartialUpdateInstancePayload) GetLabelsOk() (*map[string]*string, bool) { … }
func (o *PartialUpdateInstancePayload) SetLabels(v map[string]*string) { … }

Schemas using Labels (non-nullable additionalProperties) — zero diff vs. main:

// Instance, Token, TokenMetadata, CreateInstancePayload, CreateTokenPayload
Labels *map[string]string `json:"labels,omitempty"`

go build ./... on the generated module succeeds. A pre-existing unused-import warning in client.go is unrelated to this change (verified by regenerating with the unpatched template — same warning).

Breaking change note

For any service already on the compat-layer path whose spec uses additionalProperties: { ..., nullable: true }, the emitted Go type changes from *map[string]T to *map[string]*T. This is a breaking change for downstream Go consumers of those SDKs, but it's the correct behavior — without it, callers can't represent null values at all. Worth flagging in the next SDK release notes.

The compat-layer templates are already marked deprecated (Will be removed after 2026-09-30), so this change is consistent with the migration trajectory.

@PatrickKoss PatrickKoss requested a review from a team as a code owner May 7, 2026 20:28
Copy link
Copy Markdown
Member

@rubenhoenle rubenhoenle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See internal discussion

@rubenhoenle rubenhoenle closed this May 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants