Repository: deepmap/oapi-codegen
Pull request #689 (2022-07-20 00:30:04)

Fix: gorilla required header error shadowing
When `gorilla-server` is used with a required `Header` param. The template generates code with error shadowing.
```
shadow ./...
/oapi-server-gen.go:62:3: declaration of "err" shadows declaration ...
```


Patch:
diff --git a/README.md b/README.md
index 58fe1c9..5e9d984 100644
--- a/README.md
+++ b/README.md
@@ -678,15 +678,18 @@ file via the `--config` option. Please see the test under
 for an example. The structure of the file is as follows:
     
 ```yaml
-output:
-  externalref.gen.go
 package: externalref
 generate:
-  - types
-  - skip-prune
+  models: true
+  embedded-spec: true
+output-options:
+  skip-prune: true
 import-mapping:
   ./packageA/spec.yaml: github.com/deepmap/oapi-codegen/internal/test/externalref/packageA
   ./packageB/spec.yaml: github.com/deepmap/oapi-codegen/internal/test/externalref/packageB
+output: externalref.gen.go
+output-options:
+  skip-prune: true
 ```
 
 Have a look at [`cmd/oapi-codegen/oapi-codegen.go`](https://github.com/deepmap/oapi-codegen/blob/master/cmd/oapi-codegen/oapi-codegen.go#L48) 
diff --git a/examples/petstore-expanded/chi/api/petstore.gen.go b/examples/petstore-expanded/chi/api/petstore.gen.go
index 4a514dc..feb20aa 100644
--- a/examples/petstore-expanded/chi/api/petstore.gen.go
+++ b/examples/petstore-expanded/chi/api/petstore.gen.go
@@ -95,9 +95,6 @@ func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Reque
 	var params FindPetsParams
 
 	// ------------- Optional query parameter "tags" -------------
-	if paramValue := r.URL.Query().Get("tags"); paramValue != "" {
-
-	}
 
 	err = runtime.BindQueryParameter("form", true, false, "tags", r.URL.Query(), &params.Tags)
 	if err != nil {
@@ -106,9 +103,6 @@ func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Reque
 	}
 
 	// ------------- Optional query parameter "limit" -------------
-	if paramValue := r.URL.Query().Get("limit"); paramValue != "" {
-
-	}
 
 	err = runtime.BindQueryParameter("form", true, false, "limit", r.URL.Query(), &params.Limit)
 	if err != nil {
diff --git a/examples/petstore-expanded/gin/api/petstore-server.gen.go b/examples/petstore-expanded/gin/api/petstore-server.gen.go
index eeba01a..09ec102 100644
--- a/examples/petstore-expanded/gin/api/petstore-server.gen.go
+++ b/examples/petstore-expanded/gin/api/petstore-server.gen.go
@@ -51,9 +51,6 @@ func (siw *ServerInterfaceWrapper) FindPets(c *gin.Context) {
 	var params FindPetsParams
 
 	// ------------- Optional query parameter "tags" -------------
-	if paramValue := c.Query("tags"); paramValue != "" {
-
-	}
 
 	err = runtime.BindQueryParameter("form", true, false, "tags", c.Request.URL.Query(), &params.Tags)
 	if err != nil {
@@ -62,9 +59,6 @@ func (siw *ServerInterfaceWrapper) FindPets(c *gin.Context) {
 	}
 
 	// ------------- Optional query parameter "limit" -------------
-	if paramValue := c.Query("limit"); paramValue != "" {
-
-	}
 
 	err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), &params.Limit)
 	if err != nil {
diff --git a/examples/petstore-expanded/gorilla/api/petstore.gen.go b/examples/petstore-expanded/gorilla/api/petstore.gen.go
index 5a3e901..0531f7f 100644
--- a/examples/petstore-expanded/gorilla/api/petstore.gen.go
+++ b/examples/petstore-expanded/gorilla/api/petstore.gen.go
@@ -95,9 +95,6 @@ func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Reque
 	var params FindPetsParams
 
 	// ------------- Optional query parameter "tags" -------------
-	if paramValue := r.URL.Query().Get("tags"); paramValue != "" {
-
-	}
 
 	err = runtime.BindQueryParameter("form", true, false, "tags", r.URL.Query(), &params.Tags)
 	if err != nil {
@@ -106,9 +103,6 @@ func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Reque
 	}
 
 	// ------------- Optional query parameter "limit" -------------
-	if paramValue := r.URL.Query().Get("limit"); paramValue != "" {
-
-	}
 
 	err = runtime.BindQueryParameter("form", true, false, "limit", r.URL.Query(), &params.Limit)
 	if err != nil {
diff --git a/examples/petstore-expanded/strict/api/petstore-server.gen.go b/examples/petstore-expanded/strict/api/petstore-server.gen.go
index 7c10ee2..b229efd 100644
--- a/examples/petstore-expanded/strict/api/petstore-server.gen.go
+++ b/examples/petstore-expanded/strict/api/petstore-server.gen.go
@@ -55,9 +55,6 @@ func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Reque
 	var params FindPetsParams
 
 	// ------------- Optional query parameter "tags" -------------
-	if paramValue := r.URL.Query().Get("tags"); paramValue != "" {
-
-	}
 
 	err = runtime.BindQueryParameter("form", true, false, "tags", r.URL.Query(), &params.Tags)
 	if err != nil {
@@ -66,9 +63,6 @@ func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Reque
 	}
 
 	// ------------- Optional query parameter "limit" -------------
-	if paramValue := r.URL.Query().Get("limit"); paramValue != "" {
-
-	}
 
 	err = runtime.BindQueryParameter("form", true, false, "limit", r.URL.Query(), &params.Limit)
 	if err != nil {
@@ -376,13 +370,30 @@ type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.
 
 type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
 
+type StrictChiServerOptions struct {
+	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
+	ResponseErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
+}
+
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
-	return &strictHandler{ssi: ssi, middlewares: middlewares}
+	return &strictHandler{ssi: ssi, middlewares: middlewares, options: StrictChiServerOptions{
+		RequestErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
+			http.Error(w, err.Error(), http.StatusBadRequest)
+		},
+		ResponseErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		},
+	}}
+}
+
+func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictChiServerOptions) ServerInterface {
+	return &strictHandler{ssi: ssi, middlewares: middlewares, options: options}
 }
 
 type strictHandler struct {
 	ssi         StrictServerInterface
 	middlewares []StrictMiddlewareFunc
+	options     StrictChiServerOptions
 }
 
 // FindPets operation middleware
@@ -410,10 +421,10 @@ func (sh *strictHandler) FindPets(w http.ResponseWriter, r *http.Request, params
 		w.WriteHeader(v.StatusCode)
 		writeJSON(w, v)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -423,7 +434,7 @@ func (sh *strictHandler) AddPet(w http.ResponseWriter, r *http.Request) {
 
 	var body AddPetJSONRequestBody
 	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
-		http.Error(w, "can't decode JSON body: "+err.Error(), http.StatusBadRequest)
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
 		return
 	}
 	request.Body = &body
@@ -447,10 +458,10 @@ func (sh *strictHandler) AddPet(w http.ResponseWriter, r *http.Request) {
 		w.WriteHeader(v.StatusCode)
 		writeJSON(w, v)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -477,10 +488,10 @@ func (sh *strictHandler) DeletePet(w http.ResponseWriter, r *http.Request, id in
 		w.WriteHeader(v.StatusCode)
 		writeJSON(w, v)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -509,10 +520,10 @@ func (sh *strictHandler) FindPetByID(w http.ResponseWriter, r *http.Request, id
 		w.WriteHeader(v.StatusCode)
 		writeJSON(w, v)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go
index 6d9f345..7d03752 100644
--- a/pkg/codegen/codegen.go
+++ b/pkg/codegen/codegen.go
@@ -621,9 +621,10 @@ func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error)
 				wrapper = `"`
 			}
 			enums = append(enums, EnumDefinition{
-				Schema:       tp.Schema,
-				TypeName:     tp.TypeName,
-				ValueWrapper: wrapper,
+				Schema:         tp.Schema,
+				TypeName:       tp.TypeName,
+				ValueWrapper:   wrapper,
+				PrefixTypeName: globalState.options.Compatibility.AlwaysPrefixEnumValues,
 			})
 		}
 	}
@@ -640,8 +641,8 @@ func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error)
 			for e1key := range e1.GetValues() {
 				_, found := e2.GetValues()[e1key]
 				if found {
-					e1.Conflicts = true
-					e2.Conflicts = true
+					e1.PrefixTypeName = true
+					e2.PrefixTypeName = true
 					enums[i] = e1
 					enums[j] = e2
 					break
@@ -657,7 +658,7 @@ func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error)
 			}
 			_, found := e1.Schema.EnumValues[tp.TypeName]
 			if found {
-				e1.Conflicts = true
+				e1.PrefixTypeName = true
 				enums[i] = e1
 			}
 		}
@@ -666,7 +667,7 @@ func GenerateEnums(t *template.Template, types []TypeDefinition) (string, error)
 		// type name.
 		_, found := e1.GetValues()[e1.TypeName]
 		if found {
-			e1.Conflicts = true
+			e1.PrefixTypeName = true
 			enums[i] = e1
 		}
 	}
@@ -819,7 +820,6 @@ func GetTypeDefinitionsImports(swagger *openapi3.T, excludeSchemas []string) (ma
 }
 
 func GetSchemaImports(schemas map[string]*openapi3.SchemaRef, excludeSchemas []string) (map[string]goImport, error) {
-	var err error
 	res := map[string]goImport{}
 	excludeSchemasMap := make(map[string]bool)
 	for _, schema := range excludeSchemas {
@@ -835,17 +835,20 @@ func GetSchemaImports(schemas map[string]*openapi3.SchemaRef, excludeSchemas []s
 			continue
 		}
 
-		res, err = GetImports(schema.Properties)
+		imprts, err := GetImports(schema.Properties)
 		if err != nil {
 			return nil, err
 		}
+
+		for s, gi := range imprts {
+			res[s] = gi
+		}
 	}
 	return res, nil
 }
 
 func GetRequestBodiesImports(bodies map[string]*openapi3.RequestBodyRef) (map[string]goImport, error) {
-	var res map[string]goImport
-	var err error
+	res := map[string]goImport{}
 	for _, requestBodyName := range SortedRequestBodyKeys(bodies) {
 		requestBodyRef := bodies[requestBodyName]
 		response := requestBodyRef.Value
@@ -856,18 +859,21 @@ func GetRequestBodiesImports(bodies map[string]*openapi3.RequestBodyRef) (map[st
 				continue
 			}
 
-			res, err = GetImports(schema.Value.Properties)
+			imprts, err := GetImports(schema.Value.Properties)
 			if err != nil {
 				return nil, err
 			}
+
+			for s, gi := range imprts {
+				res[s] = gi
+			}
 		}
 	}
 	return res, nil
 }
 
 func GetResponsesImports(responses map[string]*openapi3.ResponseRef) (map[string]goImport, error) {
-	var res map[string]goImport
-	var err error
+	res := map[string]goImport{}
 	for _, responseName := range SortedResponsesKeys(responses) {
 		responseOrRef := responses[responseName]
 		response := responseOrRef.Value
@@ -878,10 +884,14 @@ func GetResponsesImports(responses map[string]*openapi3.ResponseRef) (map[string
 				continue
 			}
 
-			res, err = GetImports(schema.Value.Properties)
+			imprts, err := GetImports(schema.Value.Properties)
 			if err != nil {
 				return nil, err
 			}
+
+			for s, gi := range imprts {
+				res[s] = gi
+			}
 		}
 	}
 	return res, nil
diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go
index c8530c8..727d813 100644
--- a/pkg/codegen/configuration.go
+++ b/pkg/codegen/configuration.go
@@ -26,7 +26,7 @@ type GenerateOptions struct {
 	EchoServer    bool `yaml:"echo-server,omitempty"`    // EchoServer specifies whether to generate echo server boilerplate
 	GinServer     bool `yaml:"gin-server,omitempty"`     // GinServer specifies whether to generate gin server boilerplate
 	GorillaServer bool `yaml:"gorilla-server,omitempty"` // GorillaServer specifies whether to generate Gorilla server boilerplate
-	Strict        bool `yaml:"strict-server,omitempty"` // Strict specifies whether to generate strict server wrapper
+	Strict        bool `yaml:"strict-server,omitempty"`  // Strict specifies whether to generate strict server wrapper
 	Client        bool `yaml:"client,omitempty"`         // Client specifies whether to generate client boilerplate
 	Models        bool `yaml:"models,omitempty"`         // Models specifies whether to generate type definitions
 	EmbeddedSpec  bool `yaml:"embedded-spec,omitempty"`  // Whether to embed the swagger spec in the generated code
@@ -57,6 +57,9 @@ type CompatibilityOptions struct {
 	// When an object contains no members, and only an additionalProperties specification,
 	// it is flattened to a map. Set
 	DisableFlattenAdditionalProperties bool `yaml:"disable-flatten-additional-properties,omitempty"`
+	// When set to true, always prefix enum values with their type name instead of only
+	// when typenames would be conflicting.
+	AlwaysPrefixEnumValues bool `yaml:"always-prefix-enum-values,omitempty"`
 }
 
 // OutputOptions are used to modify the output code in some way.
diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go
index 32c50c0..3d63f85 100644
--- a/pkg/codegen/operations.go
+++ b/pkg/codegen/operations.go
@@ -615,7 +615,8 @@ func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBody
 	var bodyDefinitions []RequestBodyDefinition
 	var typeDefinitions []TypeDefinition
 
-	for contentType, content := range body.Content {
+	for _, contentType := range SortedContentKeys(body.Content) {
+		content := body.Content[contentType]
 		var tag string
 		var defaultBody bool
 
diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go
index f8da94f..ce5f586 100644
--- a/pkg/codegen/schema.go
+++ b/pkg/codegen/schema.go
@@ -111,15 +111,17 @@ type EnumDefinition struct {
 	// ValueWrapper wraps the value. It's used to conditionally apply quotes
 	// around strings.
 	ValueWrapper string
-	// Conflicts is set to true when this enum conflicts with another in
-	// terms of TypeNames
-	Conflicts bool
+	// PrefixTypeName determines if the enum value is prefixed with its TypeName.
+	// This is set to true when this enum conflicts with another in terms of
+	// TypeNames or when explicitly requested via the
+	// `compatibility.always-prefix-enum-values` option.
+	PrefixTypeName bool
 }
 
 // GetValues generates enum names in a way to minimize global conflicts
 func (e *EnumDefinition) GetValues() map[string]string {
 	// in case there are no conflicts, it's safe to use the values as-is
-	if !e.Conflicts {
+	if !e.PrefixTypeName {
 		return e.Schema.EnumValues
 	}
 	// If we do have conflicts, we will prefix the enum's typename to the values.
diff --git a/pkg/codegen/templates/chi/chi-middleware.tmpl b/pkg/codegen/templates/chi/chi-middleware.tmpl
index 30270cc..7311ec4 100644
--- a/pkg/codegen/templates/chi/chi-middleware.tmpl
+++ b/pkg/codegen/templates/chi/chi-middleware.tmpl
@@ -47,27 +47,32 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ
     // Parameter object where we will unmarshal all parameters from the context
     var params {{.OperationId}}Params
 
-    {{range $paramIdx, $param := .QueryParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
-      if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" {
+    {{range $paramIdx, $param := .QueryParams}}
+      {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}}
+        // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
+      {{ end }}
+      {{ if (or (or .Required .IsPassThrough) .IsJson) }}
+        if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" {
 
-      {{if .IsPassThrough}}
-        params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
-      {{end}}
+        {{if .IsPassThrough}}
+          params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
+        {{end}}
 
-      {{if .IsJson}}
-        var value {{.TypeDef}}
-        err = json.Unmarshal([]byte(paramValue), &value)
-        if err != nil {
-          siw.ErrorHandlerFunc(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err})
-          return
-        }
+        {{if .IsJson}}
+          var value {{.TypeDef}}
+          err = json.Unmarshal([]byte(paramValue), &value)
+          if err != nil {
+            siw.ErrorHandlerFunc(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err})
+            return
+          }
 
-        params.{{.GoName}} = {{if not .Required}}&{{end}}value
+          params.{{.GoName}} = {{if not .Required}}&{{end}}value
+        {{end}}
+        }{{if .Required}} else {
+            siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"})
+            return
+        }{{end}}
       {{end}}
-      }{{if .Required}} else {
-          siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"})
-          return
-      }{{end}}
       {{if .IsStyled}}
       err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), &params.{{.GoName}})
       if err != nil {
diff --git a/pkg/codegen/templates/echo/echo-wrappers.tmpl b/pkg/codegen/templates/echo/echo-wrappers.tmpl
index 537751a..3402583 100644
--- a/pkg/codegen/templates/echo/echo-wrappers.tmpl
+++ b/pkg/codegen/templates/echo/echo-wrappers.tmpl
@@ -32,7 +32,10 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error {
 {{if .RequiresParamObject}}
     // Parameter object where we will unmarshal all parameters from the context
     var params {{.OperationId}}Params
-{{range $paramIdx, $param := .QueryParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
+{{range $paramIdx, $param := .QueryParams}}
+    {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}}
+      // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
+    {{ end }}
     {{if .IsStyled}}
     err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", ctx.QueryParams(), &params.{{.GoName}})
     if err != nil {
diff --git a/pkg/codegen/templates/gin/gin-wrappers.tmpl b/pkg/codegen/templates/gin/gin-wrappers.tmpl
index 27bf19c..59d03f5 100644
--- a/pkg/codegen/templates/gin/gin-wrappers.tmpl
+++ b/pkg/codegen/templates/gin/gin-wrappers.tmpl
@@ -46,27 +46,32 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) {
     // Parameter object where we will unmarshal all parameters from the context
     var params {{.OperationId}}Params
 
-    {{range $paramIdx, $param := .QueryParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
-      if paramValue := c.Query("{{.ParamName}}"); paramValue != "" {
+    {{range $paramIdx, $param := .QueryParams}}
+      {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}}
+        // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
+      {{ end }}
+      {{ if (or (or .Required .IsPassThrough) .IsJson) }}
+        if paramValue := c.Query("{{.ParamName}}"); paramValue != "" {
 
-      {{if .IsPassThrough}}
-        params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
-      {{end}}
+        {{if .IsPassThrough}}
+          params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
+        {{end}}
 
-      {{if .IsJson}}
-        var value {{.TypeDef}}
-        err = json.Unmarshal([]byte(paramValue), &value)
-        if err != nil {
-          c.JSON(http.StatusBadRequest, gin.H{"msg": "Error unmarshaling parameter '{{.ParamName}}' as JSON"})
-          return
-        }
+        {{if .IsJson}}
+          var value {{.TypeDef}}
+          err = json.Unmarshal([]byte(paramValue), &value)
+          if err != nil {
+            c.JSON(http.StatusBadRequest, gin.H{"msg": "Error unmarshaling parameter '{{.ParamName}}' as JSON"})
+            return
+          }
 
-        params.{{.GoName}} = {{if not .Required}}&{{end}}value
+          params.{{.GoName}} = {{if not .Required}}&{{end}}value
+        {{end}}
+        }{{if .Required}} else {
+            c.JSON(http.StatusBadRequest, gin.H{"msg": "Query argument {{.ParamName}} is required, but not found"})
+            return
+        }{{end}}
       {{end}}
-      }{{if .Required}} else {
-          c.JSON(http.StatusBadRequest, gin.H{"msg": "Query argument {{.ParamName}} is required, but not found"})
-          return
-      }{{end}}
       {{if .IsStyled}}
       err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", c.Request.URL.Query(), &params.{{.GoName}})
       if err != nil {
diff --git a/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl b/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl
index a08bc5e..d660a0b 100644
--- a/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl
+++ b/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl
@@ -47,27 +47,32 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ
     // Parameter object where we will unmarshal all parameters from the context
     var params {{.OperationId}}Params
 
-    {{range $paramIdx, $param := .QueryParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
-      if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" {
+    {{range $paramIdx, $param := .QueryParams}}
+      {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}}
+        // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
+      {{ end }}
+      {{ if (or (or .Required .IsPassThrough) .IsJson) }}
+        if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" {
 
-      {{if .IsPassThrough}}
-        params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
-      {{end}}
+        {{if .IsPassThrough}}
+          params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
+        {{end}}
 
-      {{if .IsJson}}
-        var value {{.TypeDef}}
-        err = json.Unmarshal([]byte(paramValue), &value)
-        if err != nil {
-          siw.ErrorHandlerFunc(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err})
-          return
-        }
+        {{if .IsJson}}
+          var value {{.TypeDef}}
+          err = json.Unmarshal([]byte(paramValue), &value)
+          if err != nil {
+            siw.ErrorHandlerFunc(w, r, &UnmarshalingParamError{ParamName: "{{.ParamName}}", Err: err})
+            return
+          }
 
-        params.{{.GoName}} = {{if not .Required}}&{{end}}value
+          params.{{.GoName}} = {{if not .Required}}&{{end}}value
+        {{end}}
+        }{{if .Required}} else {
+            siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"})
+            return
+        }{{end}}
       {{end}}
-      }{{if .Required}} else {
-          siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "{{.ParamName}}"})
-          return
-      }{{end}}
       {{if .IsStyled}}
       err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), &params.{{.GoName}})
       if err != nil {
@@ -112,7 +117,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ
           params.{{.GoName}} = {{if not .Required}}&{{end}}{{.GoName}}
 
         } {{if .Required}}else {
-            err := fmt.Errorf("Header parameter {{.ParamName}} is required, but not found")
+            err = fmt.Errorf("Header parameter {{.ParamName}} is required, but not found")
             siw.ErrorHandlerFunc(w, r, &RequiredHeaderError{ParamName: "{{.ParamName}}", Err: err})
             return
         }{{end}}
diff --git a/pkg/codegen/templates/strict/strict-chi.tmpl b/pkg/codegen/templates/strict/strict-chi.tmpl
index ba2da83..c8465db 100644
--- a/pkg/codegen/templates/strict/strict-chi.tmpl
+++ b/pkg/codegen/templates/strict/strict-chi.tmpl
@@ -2,13 +2,30 @@ type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.
 
 type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
 
+type StrictChiServerOptions struct {
+    RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
+    ResponseErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
+}
+
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
-    return &strictHandler{ssi: ssi, middlewares: middlewares}
+    return &strictHandler{ssi: ssi, middlewares: middlewares, options: StrictChiServerOptions {
+        RequestErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
+            http.Error(w, err.Error(), http.StatusBadRequest)
+        },
+        ResponseErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
+            http.Error(w, err.Error(), http.StatusInternalServerError)
+        },
+    }}
+}
+
+func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictChiServerOptions) ServerInterface {
+    return &strictHandler{ssi: ssi, middlewares: middlewares, options: options}
 }
 
 type strictHandler struct {
     ssi StrictServerInterface
     middlewares []StrictMiddlewareFunc
+    options StrictChiServerOptions
 }
 
 {{range .}}
@@ -36,24 +53,24 @@ type strictHandler struct {
                 {{if eq .NameTag "JSON" -}}
                     var body {{$opid}}{{.NameTag}}RequestBody
                     if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
-                        http.Error(w, "can't decode JSON body: " + err.Error(), http.StatusBadRequest)
+                        sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
                         return
                     }
                     request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body
                 {{else if eq .NameTag "Formdata" -}}
                     if err := r.ParseForm(); err != nil {
-                        http.Error(w, "can't decode formdata: " + err.Error(), http.StatusBadRequest)
+                        sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode formdata: %w", err))
                         return
                     }
                     var body {{$opid}}{{.NameTag}}RequestBody
                     if err := runtime.BindForm(&body, r.Form, nil, nil); err != nil {
-                        http.Error(w, "can't bind formdata: " + err.Error(), http.StatusBadRequest)
+                        sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't bind formdata: %w", err))
                         return
                     }
                     request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body
                 {{else if eq .NameTag "Multipart" -}}
                     if reader, err := r.MultipartReader(); err != nil {
-                        http.Error(w, "can't decode multipart body: " + err.Error(), http.StatusBadRequest)
+                        sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode multipart body: %w", err))
                         return
                     } else {
                         request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = reader
@@ -61,7 +78,7 @@ type strictHandler struct {
                 {{else if eq .NameTag "Text" -}}
                     data, err := ioutil.ReadAll(r.Body)
                     if err != nil {
-                        http.Error(w, "can't read body: " + err.Error(), http.StatusBadRequest)
+                        sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't read body: %w", err))
                         return
                     }
                     body := {{$opid}}{{.NameTag}}RequestBody(data)
@@ -114,7 +131,7 @@ type strictHandler struct {
                     {{else if eq .NameTag "Multipart" -}}
                         defer writer.Close()
                         if err := v(writer); err != nil {
-                            http.Error(w, err.Error(), http.StatusInternalServerError)
+                            sh.options.ResponseErrorHandlerFunc(w, r, err)
                         }
                     {{else -}}
                         if closer, ok := v.Body.(io.ReadCloser); ok {
@@ -132,10 +149,10 @@ type strictHandler struct {
                 {{end}}{{/* if eq 0 (len .Contents) */ -}}
             {{end}}{{/* range .Responses */ -}}
             case error:
-                http.Error(w, v.Error(), http.StatusInternalServerError)
+                sh.options.ResponseErrorHandlerFunc(w, r, v)
             case nil:
             default:
-                http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+                sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
         }
     }
 {{end}}
diff --git a/pkg/codegen/templates/union.tmpl b/pkg/codegen/templates/union.tmpl
index a29e2d0..ac7657d 100644
--- a/pkg/codegen/templates/union.tmpl
+++ b/pkg/codegen/templates/union.tmpl
@@ -65,9 +65,11 @@
                 return nil, err
             }
             object := make(map[string]json.RawMessage)
-            err = json.Unmarshal(b, &object)
-            if err != nil {
+            if t.union != nil {
+              err = json.Unmarshal(b, &object)
+              if err != nil {
                 return nil, err
+              }
             }
             {{range .Schema.Properties}}
                 object["{{.JsonFieldName}}"], err = json.Marshal(t.{{.GoFieldName}})
diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go
index afa2059..e5f79fe 100644
--- a/pkg/codegen/utils.go
+++ b/pkg/codegen/utils.go
@@ -604,6 +604,8 @@ func typeNamePrefix(name string) (prefix string) {
 			prefix += "Asterisk"
 		case '^':
 			prefix += "Caret"
+		case '%':
+			prefix += "Percent"
 		default:
 			// Prepend "N" to schemas starting with a number
 			if prefix == "" && unicode.IsDigit(r) {


Test patch:
diff --git a/internal/test/components/components.gen.go b/internal/test/components/components.gen.go
index 37b0cef..76aa43f 100644
--- a/internal/test/components/components.gen.go
+++ b/internal/test/components/components.gen.go
@@ -66,6 +66,7 @@ const (
 	FunnyValuesAsterisk FunnyValues = "*"
 	FunnyValuesEmpty    FunnyValues = ""
 	FunnyValuesN5       FunnyValues = "5"
+	FunnyValuesPercent  FunnyValues = "%"
 )
 
 // Defines values for EnumParam1.
@@ -349,14 +350,14 @@ type RenamedRequestBody struct {
 	Field SchemaObject `json:"Field"`
 }
 
+// EnsureEverythingIsReferencedTextBody defines parameters for EnsureEverythingIsReferenced.
+type EnsureEverythingIsReferencedTextBody = string
+
 // EnsureEverythingIsReferencedJSONBody defines parameters for EnsureEverythingIsReferenced.
 type EnsureEverythingIsReferencedJSONBody struct {
 	Field SchemaObject `json:"Field"`
 }
 
-// EnsureEverythingIsReferencedTextBody defines parameters for EnsureEverythingIsReferenced.
-type EnsureEverythingIsReferencedTextBody = string
-
 // ParamsWithAddPropsParams defines parameters for ParamsWithAddProps.
 type ParamsWithAddPropsParams struct {
 	// This parameter has additional properties
@@ -879,9 +880,11 @@ func (t OneOfObject10) MarshalJSON() ([]byte, error) {
 		return nil, err
 	}
 	object := make(map[string]json.RawMessage)
-	err = json.Unmarshal(b, &object)
-	if err != nil {
-		return nil, err
+	if t.union != nil {
+		err = json.Unmarshal(b, &object)
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	object["one"], err = json.Marshal(t.One)
@@ -1175,9 +1178,11 @@ func (t OneOfObject4) MarshalJSON() ([]byte, error) {
 		return nil, err
 	}
 	object := make(map[string]json.RawMessage)
-	err = json.Unmarshal(b, &object)
-	if err != nil {
-		return nil, err
+	if t.union != nil {
+		err = json.Unmarshal(b, &object)
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	object["fixedProperty"], err = json.Marshal(t.FixedProperty)
@@ -1374,9 +1379,11 @@ func (t OneOfObject8) MarshalJSON() ([]byte, error) {
 		return nil, err
 	}
 	object := make(map[string]json.RawMessage)
-	err = json.Unmarshal(b, &object)
-	if err != nil {
-		return nil, err
+	if t.union != nil {
+		err = json.Unmarshal(b, &object)
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	object["fixed"], err = json.Marshal(t.Fixed)
@@ -1465,9 +1472,11 @@ func (t OneOfObject9) MarshalJSON() ([]byte, error) {
 		return nil, err
 	}
 	object := make(map[string]json.RawMessage)
-	err = json.Unmarshal(b, &object)
-	if err != nil {
-		return nil, err
+	if t.union != nil {
+		err = json.Unmarshal(b, &object)
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	object["type"], err = json.Marshal(t.Type)
diff --git a/internal/test/components/components.yaml b/internal/test/components/components.yaml
index f5d6441..f49173a 100644
--- a/internal/test/components/components.yaml
+++ b/internal/test/components/components.yaml
@@ -429,6 +429,7 @@ components:
         - '*'
         - '5'
         - '&'
+        - '%'
         - ''
     RenameMe:
       description: This schema should be renamed via x-go-name when generating
diff --git a/internal/test/components/components_test.go b/internal/test/components/components_test.go
index b4f8933..bcb34d7 100644
--- a/internal/test/components/components_test.go
+++ b/internal/test/components/components_test.go
@@ -202,3 +202,13 @@ func TestAnyOf(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, OneOfVariant5{Discriminator: "all", Id: 456}, v5)
 }
+
+func TestMarshalWhenNoUnionValueSet(t *testing.T) {
+	const expected = `{"one":null,"three":null,"two":null}`
+
+	var dst OneOfObject10
+
+	bytes, err := dst.MarshalJSON()
+	assert.Nil(t, err)
+	assert.Equal(t, expected, string(bytes))
+}
diff --git a/internal/test/issues/issue-illegal_enum_names/issue.gen.go b/internal/test/issues/issue-illegal_enum_names/issue.gen.go
index cf58178..35dcbe1 100644
--- a/internal/test/issues/issue-illegal_enum_names/issue.gen.go
+++ b/internal/test/issues/issue-illegal_enum_names/issue.gen.go
@@ -23,6 +23,7 @@ import (
 // Defines values for Bar.
 const (
 	BarBar     Bar = "Bar"
+	BarEmpty   Bar = ""
 	BarFoo     Bar = "Foo"
 	BarFoo1    Bar = " Foo"
 	BarFoo2    Bar = " Foo "
@@ -313,10 +314,10 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
 var swaggerSpec = []string{
 
 	"H4sIAAAAAAAC/0xQQU4DMQz8SjVwDNml3HLkUMQbEKqirbcN6tpRYpCqKn9HzkIhl5kkHtszV0yyZGFi",
-	"rQhX1OlES+z0ORYD4s8F4Q07Ebj+6IxvbuxhZY//CzbrxWADh/1OZG81eHfQSyYEVC2Jj2itOSSexWZp",
-	"0rP9ee/h8EWlJmEEjH70I5qDZOKYEwKe/Oi3cMhRT33dYZbe40hqIJlK1CT8ekDAC+m6UaGahSt1yXYc",
-	"DSZhJe6qmPM5TV03fFSb/RuJsaS0dOF9oRkBd8NfeMNPcoMF0G4uYynxspo8UJ1KyrpaMoutn+8AAAD/",
-	"/y6OlsyDAQAA",
+	"rQhX1OlES+z0ORYD4s8F4Q1w2InA9ffONzf2sLLH/wWb9WKwgcN+J7K3Grw76CUTAqqWxEe01hwSz2Lj",
+	"NOnZ/rz3cPiiUpMwAkY/+hHNQTJxzAkBT370WzjkqKe+8TBL73EkNZBMJWoSfj0g4IV03ahQzcKVumQ7",
+	"jgaTsBJ3Vcz5nKauGz6qzf5NxVhSWrrwvtCMgLvhL7/hJ7zBAmg3l7GUeFlNHqhOJWVdLZnF1s93AAAA",
+	"//9U8KAOhgEAAA==",
 }
 
 // GetSwagger returns the content of the embedded swagger specification file
diff --git a/internal/test/issues/issue-illegal_enum_names/issue_test.go b/internal/test/issues/issue-illegal_enum_names/issue_test.go
index 8c40e09..c924eaa 100644
--- a/internal/test/issues/issue-illegal_enum_names/issue_test.go
+++ b/internal/test/issues/issue-illegal_enum_names/issue_test.go
@@ -47,6 +47,7 @@ func TestIllegalEnumNames(t *testing.T) {
 		}
 	}
 
+	require.Equal(t, `""`, constDefs["BarEmpty"])
 	require.Equal(t, `"Bar"`, constDefs["BarBar"])
 	require.Equal(t, `"Foo"`, constDefs["BarFoo"])
 	require.Equal(t, `"Foo Bar"`, constDefs["BarFooBar"])
diff --git a/internal/test/issues/issue-illegal_enum_names/spec.yaml b/internal/test/issues/issue-illegal_enum_names/spec.yaml
index cf3fc78..d222a57 100644
--- a/internal/test/issues/issue-illegal_enum_names/spec.yaml
+++ b/internal/test/issues/issue-illegal_enum_names/spec.yaml
@@ -22,6 +22,7 @@ components:
     Bar:
       type: string
       enum:
+        - ''
         - Foo
         - Bar
         - Foo Bar
diff --git a/internal/test/server/server.gen.go b/internal/test/server/server.gen.go
index bd747ca..ed4a92f 100644
--- a/internal/test/server/server.gen.go
+++ b/internal/test/server/server.gen.go
@@ -211,9 +211,6 @@ func (siw *ServerInterfaceWrapper) GetWithArgs(w http.ResponseWriter, r *http.Re
 	var params GetWithArgsParams
 
 	// ------------- Optional query parameter "optional_argument" -------------
-	if paramValue := r.URL.Query().Get("optional_argument"); paramValue != "" {
-
-	}
 
 	err = runtime.BindQueryParameter("form", true, false, "optional_argument", r.URL.Query(), &params.OptionalArgument)
 	if err != nil {
@@ -222,6 +219,7 @@ func (siw *ServerInterfaceWrapper) GetWithArgs(w http.ResponseWriter, r *http.Re
 	}
 
 	// ------------- Required query parameter "required_argument" -------------
+
 	if paramValue := r.URL.Query().Get("required_argument"); paramValue != "" {
 
 	} else {
@@ -388,9 +386,6 @@ func (siw *ServerInterfaceWrapper) CreateResource2(w http.ResponseWriter, r *htt
 	var params CreateResource2Params
 
 	// ------------- Optional query parameter "inline_query_argument" -------------
-	if paramValue := r.URL.Query().Get("inline_query_argument"); paramValue != "" {
-
-	}
 
 	err = runtime.BindQueryParameter("form", true, false, "inline_query_argument", r.URL.Query(), &params.InlineQueryArgument)
 	if err != nil {
diff --git a/internal/test/strict-server/chi/server.gen.go b/internal/test/strict-server/chi/server.gen.go
index 32f2692..a40b603 100644
--- a/internal/test/strict-server/chi/server.gen.go
+++ b/internal/test/strict-server/chi/server.gen.go
@@ -597,13 +597,30 @@ type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.
 
 type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
 
+type StrictChiServerOptions struct {
+	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
+	ResponseErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
+}
+
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
-	return &strictHandler{ssi: ssi, middlewares: middlewares}
+	return &strictHandler{ssi: ssi, middlewares: middlewares, options: StrictChiServerOptions{
+		RequestErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
+			http.Error(w, err.Error(), http.StatusBadRequest)
+		},
+		ResponseErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) {
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+		},
+	}}
+}
+
+func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictChiServerOptions) ServerInterface {
+	return &strictHandler{ssi: ssi, middlewares: middlewares, options: options}
 }
 
 type strictHandler struct {
 	ssi         StrictServerInterface
 	middlewares []StrictMiddlewareFunc
+	options     StrictChiServerOptions
 }
 
 // JSONExample operation middleware
@@ -612,7 +629,7 @@ func (sh *strictHandler) JSONExample(w http.ResponseWriter, r *http.Request) {
 
 	var body JSONExampleJSONRequestBody
 	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
-		http.Error(w, "can't decode JSON body: "+err.Error(), http.StatusBadRequest)
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
 		return
 	}
 	request.Body = &body
@@ -636,10 +653,10 @@ func (sh *strictHandler) JSONExample(w http.ResponseWriter, r *http.Request) {
 	case JSONExampledefaultResponse:
 		w.WriteHeader(v.StatusCode)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -648,7 +665,7 @@ func (sh *strictHandler) MultipartExample(w http.ResponseWriter, r *http.Request
 	var request MultipartExampleRequestObject
 
 	if reader, err := r.MultipartReader(); err != nil {
-		http.Error(w, "can't decode multipart body: "+err.Error(), http.StatusBadRequest)
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode multipart body: %w", err))
 		return
 	} else {
 		request.Body = reader
@@ -670,17 +687,17 @@ func (sh *strictHandler) MultipartExample(w http.ResponseWriter, r *http.Request
 		w.WriteHeader(200)
 		defer writer.Close()
 		if err := v(writer); err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
 		}
 	case MultipartExample400Response:
 		w.WriteHeader(400)
 	case MultipartExampledefaultResponse:
 		w.WriteHeader(v.StatusCode)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -691,19 +708,19 @@ func (sh *strictHandler) MultipleRequestAndResponseTypes(w http.ResponseWriter,
 	if strings.HasPrefix(r.Header.Get("Content-Type"), "application/json") {
 		var body MultipleRequestAndResponseTypesJSONRequestBody
 		if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
-			http.Error(w, "can't decode JSON body: "+err.Error(), http.StatusBadRequest)
+			sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
 			return
 		}
 		request.JSONBody = &body
 	}
 	if strings.HasPrefix(r.Header.Get("Content-Type"), "application/x-www-form-urlencoded") {
 		if err := r.ParseForm(); err != nil {
-			http.Error(w, "can't decode formdata: "+err.Error(), http.StatusBadRequest)
+			sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode formdata: %w", err))
 			return
 		}
 		var body MultipleRequestAndResponseTypesFormdataRequestBody
 		if err := runtime.BindForm(&body, r.Form, nil, nil); err != nil {
-			http.Error(w, "can't bind formdata: "+err.Error(), http.StatusBadRequest)
+			sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't bind formdata: %w", err))
 			return
 		}
 		request.FormdataBody = &body
@@ -713,7 +730,7 @@ func (sh *strictHandler) MultipleRequestAndResponseTypes(w http.ResponseWriter,
 	}
 	if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/form-data") {
 		if reader, err := r.MultipartReader(); err != nil {
-			http.Error(w, "can't decode multipart body: "+err.Error(), http.StatusBadRequest)
+			sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode multipart body: %w", err))
 			return
 		} else {
 			request.MultipartBody = reader
@@ -722,7 +739,7 @@ func (sh *strictHandler) MultipleRequestAndResponseTypes(w http.ResponseWriter,
 	if strings.HasPrefix(r.Header.Get("Content-Type"), "text/plain") {
 		data, err := ioutil.ReadAll(r.Body)
 		if err != nil {
-			http.Error(w, "can't read body: "+err.Error(), http.StatusBadRequest)
+			sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't read body: %w", err))
 			return
 		}
 		body := MultipleRequestAndResponseTypesTextRequestBody(data)
@@ -767,7 +784,7 @@ func (sh *strictHandler) MultipleRequestAndResponseTypes(w http.ResponseWriter,
 		w.WriteHeader(200)
 		defer writer.Close()
 		if err := v(writer); err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
+			sh.options.ResponseErrorHandlerFunc(w, r, err)
 		}
 	case MultipleRequestAndResponseTypes200TextResponse:
 		w.Header().Set("Content-Type", "text/plain")
@@ -776,10 +793,10 @@ func (sh *strictHandler) MultipleRequestAndResponseTypes(w http.ResponseWriter,
 	case MultipleRequestAndResponseTypes400Response:
 		w.WriteHeader(400)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -789,7 +806,7 @@ func (sh *strictHandler) ReusableResponses(w http.ResponseWriter, r *http.Reques
 
 	var body ReusableResponsesJSONRequestBody
 	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
-		http.Error(w, "can't decode JSON body: "+err.Error(), http.StatusBadRequest)
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
 		return
 	}
 	request.Body = &body
@@ -815,10 +832,10 @@ func (sh *strictHandler) ReusableResponses(w http.ResponseWriter, r *http.Reques
 	case ReusableResponsesdefaultResponse:
 		w.WriteHeader(v.StatusCode)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -828,7 +845,7 @@ func (sh *strictHandler) TextExample(w http.ResponseWriter, r *http.Request) {
 
 	data, err := ioutil.ReadAll(r.Body)
 	if err != nil {
-		http.Error(w, "can't read body: "+err.Error(), http.StatusBadRequest)
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't read body: %w", err))
 		return
 	}
 	body := TextExampleTextRequestBody(data)
@@ -853,10 +870,10 @@ func (sh *strictHandler) TextExample(w http.ResponseWriter, r *http.Request) {
 	case TextExampledefaultResponse:
 		w.WriteHeader(v.StatusCode)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -891,10 +908,10 @@ func (sh *strictHandler) UnknownExample(w http.ResponseWriter, r *http.Request)
 	case UnknownExampledefaultResponse:
 		w.WriteHeader(v.StatusCode)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -935,10 +952,10 @@ func (sh *strictHandler) UnspecifiedContentType(w http.ResponseWriter, r *http.R
 	case UnspecifiedContentTypedefaultResponse:
 		w.WriteHeader(v.StatusCode)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -947,12 +964,12 @@ func (sh *strictHandler) URLEncodedExample(w http.ResponseWriter, r *http.Reques
 	var request URLEncodedExampleRequestObject
 
 	if err := r.ParseForm(); err != nil {
-		http.Error(w, "can't decode formdata: "+err.Error(), http.StatusBadRequest)
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode formdata: %w", err))
 		return
 	}
 	var body URLEncodedExampleFormdataRequestBody
 	if err := runtime.BindForm(&body, r.Form, nil, nil); err != nil {
-		http.Error(w, "can't bind formdata: "+err.Error(), http.StatusBadRequest)
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't bind formdata: %w", err))
 		return
 	}
 	request.Body = &body
@@ -980,10 +997,10 @@ func (sh *strictHandler) URLEncodedExample(w http.ResponseWriter, r *http.Reques
 	case URLEncodedExampledefaultResponse:
 		w.WriteHeader(v.StatusCode)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
@@ -995,7 +1012,7 @@ func (sh *strictHandler) HeadersExample(w http.ResponseWriter, r *http.Request,
 
 	var body HeadersExampleJSONRequestBody
 	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
-		http.Error(w, "can't decode JSON body: "+err.Error(), http.StatusBadRequest)
+		sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err))
 		return
 	}
 	request.Body = &body
@@ -1021,10 +1038,10 @@ func (sh *strictHandler) HeadersExample(w http.ResponseWriter, r *http.Request,
 	case HeadersExampledefaultResponse:
 		w.WriteHeader(v.StatusCode)
 	case error:
-		http.Error(w, v.Error(), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, v)
 	case nil:
 	default:
-		http.Error(w, fmt.Sprintf("Unexpected response type: %T", v), http.StatusInternalServerError)
+		sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", v))
 	}
 }
 
diff --git a/pkg/codegen/codegen_test.go b/pkg/codegen/codegen_test.go
index d5840b3..ba72ceb 100644
--- a/pkg/codegen/codegen_test.go
+++ b/pkg/codegen/codegen_test.go
@@ -220,6 +220,15 @@ func TestXGoTypeImport(t *testing.T) {
 	// Check generated struct
 	assert.Contains(t, code, "type Pet struct {\n\tAge myuuid.UUID `json:\"age\"`\n}")
 
+	// Check import
+	assert.Contains(t, code, `github.com/CavernaTechnologies/pgext`)
+
+	// Check generated struct
+	assert.Contains(t, code, "type Person struct {\n\tAge pgext.Puint `json:\"age\"`\n}")
+
+	// Check generated struct
+	assert.Contains(t, code, "type Car struct {\n\tAge int `json:\"age\"`\n}")
+
 	// Make sure the generated code is valid:
 	checkLint(t, "test.gen.go", []byte(code))
 
diff --git a/pkg/codegen/test_specs/x-go-type-import-pet.yaml b/pkg/codegen/test_specs/x-go-type-import-pet.yaml
index 4fea6ff..9df66b5 100644
--- a/pkg/codegen/test_specs/x-go-type-import-pet.yaml
+++ b/pkg/codegen/test_specs/x-go-type-import-pet.yaml
@@ -28,12 +28,53 @@ paths:
             type: integer
             format: int64
       responses:
-        '200':
+        "200":
           description: pet response
           content:
             application/json:
               schema:
-                $ref: '#/components/schemas/Pet'
+                $ref: "#/components/schemas/Pet"
+  /person/{id}:
+    get:
+      summary: Returns a person by ID
+      description: Returns a person based on a single ID
+      operationId: findPersonByID
+      parameters:
+        - name: id
+          in: path
+          description: ID of person to fetch
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        "200":
+          description: person response
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/Person"
+  /car/{id}:
+    get:
+      summary: Returns a car by ID
+      description: Returns a car based on a single ID
+      operationId: findCarByID
+      parameters:
+        - name: id
+          in: path
+          description: ID of car to fetch
+          required: true
+          schema:
+            type: integer
+            format: int64
+      responses:
+        "200":
+          description: car response
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/Car"
+
 components:
   schemas:
     Pet:
@@ -45,3 +86,17 @@ components:
             name: myuuid
       required:
         - age
+    Person:
+      properties:
+        age:
+          x-go-type: pgext.Puint
+          x-go-type-import:
+            path: github.com/CavernaTechnologies/pgext
+      required:
+        - age
+    Car:
+      properties:
+        age:
+          type: integer
+      required:
+        - age