From 86b0a52d625d36efe26892dde73bc09aa91d28db Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Thu, 14 May 2026 15:46:13 +0200 Subject: [PATCH] [go] support io.Reader and []byte response types in client decode The Go generator maps binary types to *os.File by default. Callers can override that with --type-mappings to get io.Reader or []byte, and setBody already handles both for request bodies. The shared decode helper, however, only asserts *string, *os.File, and **os.File. When Execute declares localVarReturnValue io.Reader or []byte and calls decode(&localVarReturnValue, ...), no branch matches and the call returns 'undefined response type'. Add two branches: - *io.Reader wraps the already-buffered bytes in a bytes.Reader. - *[]byte assigns the bytes directly. This branch must stay before the JSON branch, since json.Unmarshal accepts *[]byte and base64-decodes into it, which is not what we want for raw binary responses. Both shapes are useful for different scenarios: []byte is more honest (the response is fully buffered in memory anyway), while io.Reader is forward-compatible (the return type stays valid if the response path is ever refactored to skip the eager buffering and stream the body). bytes is already imported in every generated client.go. The same additive patch is applied to the eleven Go client samples. --- .../src/main/resources/go/client.mustache | 9 +++++++++ samples/client/echo_api/go-external-refs/client.go | 9 +++++++++ samples/client/echo_api/go/client.go | 9 +++++++++ .../go/allof_multiple_ref_and_discriminator/client.go | 9 +++++++++ samples/client/others/go/oneof-anyof-required/client.go | 9 +++++++++ .../others/go/oneof-discriminator-lookup/client.go | 9 +++++++++ samples/client/petstore/go/go-petstore/client.go | 9 +++++++++ .../extensions/x-auth-id-alias/go-experimental/client.go | 9 +++++++++ .../go-petstore-generateMarshalJSON-false/client.go | 9 +++++++++ .../client/petstore/go-petstore-withXml/client.go | 9 +++++++++ .../petstore/go/go-petstore-aws-signature/client.go | 9 +++++++++ .../openapi3/client/petstore/go/go-petstore/client.go | 9 +++++++++ 12 files changed, 108 insertions(+) diff --git a/modules/openapi-generator/src/main/resources/go/client.mustache b/modules/openapi-generator/src/main/resources/go/client.mustache index 9c189b8dc212..282ad6ed56d8 100644 --- a/modules/openapi-generator/src/main/resources/go/client.mustache +++ b/modules/openapi-generator/src/main/resources/go/client.mustache @@ -539,6 +539,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/client/echo_api/go-external-refs/client.go b/samples/client/echo_api/go-external-refs/client.go index 01f4055c6cfe..83d93205607b 100644 --- a/samples/client/echo_api/go-external-refs/client.go +++ b/samples/client/echo_api/go-external-refs/client.go @@ -460,6 +460,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/client/echo_api/go/client.go b/samples/client/echo_api/go/client.go index 01f4055c6cfe..83d93205607b 100644 --- a/samples/client/echo_api/go/client.go +++ b/samples/client/echo_api/go/client.go @@ -460,6 +460,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/client/others/go/allof_multiple_ref_and_discriminator/client.go b/samples/client/others/go/allof_multiple_ref_and_discriminator/client.go index f797d2c4d2b3..7a19a74088d8 100644 --- a/samples/client/others/go/allof_multiple_ref_and_discriminator/client.go +++ b/samples/client/others/go/allof_multiple_ref_and_discriminator/client.go @@ -431,6 +431,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/client/others/go/oneof-anyof-required/client.go b/samples/client/others/go/oneof-anyof-required/client.go index f797d2c4d2b3..7a19a74088d8 100644 --- a/samples/client/others/go/oneof-anyof-required/client.go +++ b/samples/client/others/go/oneof-anyof-required/client.go @@ -431,6 +431,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/client/others/go/oneof-discriminator-lookup/client.go b/samples/client/others/go/oneof-discriminator-lookup/client.go index f797d2c4d2b3..7a19a74088d8 100644 --- a/samples/client/others/go/oneof-discriminator-lookup/client.go +++ b/samples/client/others/go/oneof-discriminator-lookup/client.go @@ -431,6 +431,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/client/petstore/go/go-petstore/client.go b/samples/client/petstore/go/go-petstore/client.go index 82ace87a5306..85036942f54b 100644 --- a/samples/client/petstore/go/go-petstore/client.go +++ b/samples/client/petstore/go/go-petstore/client.go @@ -466,6 +466,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/openapi3/client/extensions/x-auth-id-alias/go-experimental/client.go b/samples/openapi3/client/extensions/x-auth-id-alias/go-experimental/client.go index 9b0df1df106f..58b26ebc1573 100644 --- a/samples/openapi3/client/extensions/x-auth-id-alias/go-experimental/client.go +++ b/samples/openapi3/client/extensions/x-auth-id-alias/go-experimental/client.go @@ -434,6 +434,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/openapi3/client/petstore/go-petstore-generateMarshalJSON-false/client.go b/samples/openapi3/client/petstore/go-petstore-generateMarshalJSON-false/client.go index 2747d3b9714f..7f611c52fa4d 100644 --- a/samples/openapi3/client/petstore/go-petstore-generateMarshalJSON-false/client.go +++ b/samples/openapi3/client/petstore/go-petstore-generateMarshalJSON-false/client.go @@ -446,6 +446,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/openapi3/client/petstore/go-petstore-withXml/client.go b/samples/openapi3/client/petstore/go-petstore-withXml/client.go index 2747d3b9714f..7f611c52fa4d 100644 --- a/samples/openapi3/client/petstore/go-petstore-withXml/client.go +++ b/samples/openapi3/client/petstore/go-petstore-withXml/client.go @@ -446,6 +446,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/openapi3/client/petstore/go/go-petstore-aws-signature/client.go b/samples/openapi3/client/petstore/go/go-petstore-aws-signature/client.go index 79d1274dfd0c..73c66a0e3188 100644 --- a/samples/openapi3/client/petstore/go/go-petstore-aws-signature/client.go +++ b/samples/openapi3/client/petstore/go/go-petstore-aws-signature/client.go @@ -488,6 +488,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { diff --git a/samples/openapi3/client/petstore/go/go-petstore/client.go b/samples/openapi3/client/petstore/go/go-petstore/client.go index 259fda811873..8f341a904a36 100644 --- a/samples/openapi3/client/petstore/go/go-petstore/client.go +++ b/samples/openapi3/client/petstore/go/go-petstore/client.go @@ -484,6 +484,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil {