Repository: ogen-go/ogen
Pull request #505 (2022-07-27 04:19:01)

feat(ogenerrors): return 415 for unsupported `Content-Type`

codecov[bot]: # [Codecov](https://codecov.io/gh/ogen-go/ogen/pull/505?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go) Report
> Merging [#505](https://codecov.io/gh/ogen-go/ogen/pull/505?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go) (4ec7894) into [main](https://codecov.io/gh/ogen-go/ogen/commit/3fdf5e62187efae85e079bde811f802de738dec5?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go) (3fdf5e6) will **decrease** coverage by `0.16%`.
> The diff coverage is `0.00%`.

```diff
@@            Coverage Diff             @@
##             main     #505      +/-   ##
==========================================
- Coverage   71.36%   71.19%   -0.17%     
==========================================
  Files         151      152       +1     
  Lines       10963    10989      +26     
==========================================
  Hits         7824     7824              
- Misses       2697     2723      +26     
  Partials      442      442              
```


| [Impacted Files](https://codecov.io/gh/ogen-go/ogen/pull/505?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go) | Coverage Δ | |
|---|---|---|
| [ogenerrors/handler.go](https://codecov.io/gh/ogen-go/ogen/pull/505/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go#diff-b2dlbmVycm9ycy9oYW5kbGVyLmdv) | `0.00% <0.00%> (ø)` | |
| [ogenerrors/ogenerrors.go](https://codecov.io/gh/ogen-go/ogen/pull/505/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go#diff-b2dlbmVycm9ycy9vZ2VuZXJyb3JzLmdv) | `0.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/ogen-go/ogen/pull/505?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/ogen-go/ogen/pull/505?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go). Last update [3fdf5e6...4ec7894](https://codecov.io/gh/ogen-go/ogen/pull/505?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ogen-go).


Patch:
diff --git a/examples/ex_2ch/oas_cfg_gen.go b/examples/ex_2ch/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_2ch/oas_cfg_gen.go
+++ b/examples/ex_2ch/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_ent/oas_cfg_gen.go b/examples/ex_ent/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_ent/oas_cfg_gen.go
+++ b/examples/ex_ent/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_firecracker/oas_cfg_gen.go b/examples/ex_firecracker/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_firecracker/oas_cfg_gen.go
+++ b/examples/ex_firecracker/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_github/oas_cfg_gen.go b/examples/ex_github/oas_cfg_gen.go
index 9225570c..2ec048d3 100644
--- a/examples/ex_github/oas_cfg_gen.go
+++ b/examples/ex_github/oas_cfg_gen.go
@@ -3,17 +3,14 @@
 package api
 
 import (
-	"context"
 	"net/http"
 	"regexp"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
@@ -27,30 +24,7 @@ var regexMap = map[string]*regexp.Regexp{
 }
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -74,7 +48,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_gotd/oas_cfg_gen.go b/examples/ex_gotd/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_gotd/oas_cfg_gen.go
+++ b/examples/ex_gotd/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_k8s/oas_cfg_gen.go b/examples/ex_k8s/oas_cfg_gen.go
index 3524bd0e..3eb0465b 100644
--- a/examples/ex_k8s/oas_cfg_gen.go
+++ b/examples/ex_k8s/oas_cfg_gen.go
@@ -3,17 +3,14 @@
 package api
 
 import (
-	"context"
 	"net/http"
 	"regexp"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
@@ -23,30 +20,7 @@ var regexMap = map[string]*regexp.Regexp{
 }
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -70,7 +44,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_manga/oas_cfg_gen.go b/examples/ex_manga/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_manga/oas_cfg_gen.go
+++ b/examples/ex_manga/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_petstore/oas_cfg_gen.go b/examples/ex_petstore/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_petstore/oas_cfg_gen.go
+++ b/examples/ex_petstore/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_petstore_expanded/oas_cfg_gen.go b/examples/ex_petstore_expanded/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_petstore_expanded/oas_cfg_gen.go
+++ b/examples/ex_petstore_expanded/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_route_params/oas_cfg_gen.go b/examples/ex_route_params/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_route_params/oas_cfg_gen.go
+++ b/examples/ex_route_params/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_telegram/oas_cfg_gen.go b/examples/ex_telegram/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_telegram/oas_cfg_gen.go
+++ b/examples/ex_telegram/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/examples/ex_tinkoff/oas_cfg_gen.go b/examples/ex_tinkoff/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_tinkoff/oas_cfg_gen.go
+++ b/examples/ex_tinkoff/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/gen/_template/cfg.tmpl b/gen/_template/cfg.tmpl
index 8b7e822c..73b5bc50 100644
--- a/gen/_template/cfg.tmpl
+++ b/gen/_template/cfg.tmpl
@@ -10,30 +10,7 @@
 
 {{- if $.ServerEnabled }}
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct{
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 {{- end }}
 
 type config struct {
@@ -65,7 +42,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler: respondError,
+		ErrorHandler: ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 		{{- end }}
 	}
diff --git a/internal/referenced_path_item/oas_cfg_gen.go b/internal/referenced_path_item/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/internal/referenced_path_item/oas_cfg_gen.go
+++ b/internal/referenced_path_item/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/internal/sample_api/oas_cfg_gen.go b/internal/sample_api/oas_cfg_gen.go
index 5eda8aa2..b9a44432 100644
--- a/internal/sample_api/oas_cfg_gen.go
+++ b/internal/sample_api/oas_cfg_gen.go
@@ -3,19 +3,16 @@
 package api
 
 import (
-	"context"
 	"fmt"
 	"math/big"
 	"net/http"
 	"regexp"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
@@ -44,30 +41,7 @@ var ratMap = map[string]*big.Rat{
 }
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -91,7 +65,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/internal/sample_api_nc/oas_cfg_gen.go b/internal/sample_api_nc/oas_cfg_gen.go
index b74b5c35..0c6a1865 100644
--- a/internal/sample_api_nc/oas_cfg_gen.go
+++ b/internal/sample_api_nc/oas_cfg_gen.go
@@ -3,19 +3,15 @@
 package api
 
 import (
-	"context"
 	"fmt"
 	"math/big"
 	"net/http"
 	"regexp"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
-	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
@@ -44,30 +40,7 @@ var ratMap = map[string]*big.Rat{
 }
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -89,7 +62,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/internal/sample_err/oas_cfg_gen.go b/internal/sample_err/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/internal/sample_err/oas_cfg_gen.go
+++ b/internal/sample_err/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/internal/techempower/oas_cfg_gen.go b/internal/techempower/oas_cfg_gen.go
index 0885122e..31bd345e 100644
--- a/internal/techempower/oas_cfg_gen.go
+++ b/internal/techempower/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package techempower
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/ogenerrors/handler.go b/ogenerrors/handler.go
new file mode 100644
index 00000000..00023881
--- /dev/null
+++ b/ogenerrors/handler.go
@@ -0,0 +1,53 @@
+package ogenerrors
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/go-faster/errors"
+	"github.com/go-faster/jx"
+
+	ht "github.com/ogen-go/ogen/http"
+	"github.com/ogen-go/ogen/validate"
+)
+
+// ErrorHandler is an error handler.
+type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
+
+// ErrorCode returns HTTP code for given error.
+//
+// The default code is http.StatusInternalServerError.
+func ErrorCode(err error) (code int) {
+	code = http.StatusInternalServerError
+
+	var (
+		ctError *validate.InvalidContentTypeError
+		ogenErr Error
+	)
+	switch {
+	case errors.Is(err, ht.ErrNotImplemented):
+		code = http.StatusNotImplemented
+	case errors.As(err, &ctError):
+		// Takes precedence over Error.
+		code = http.StatusUnsupportedMediaType
+	case errors.As(err, &ogenErr):
+		code = ogenErr.Code()
+	}
+
+	return code
+}
+
+// DefaultErrorHandler is the default error handler.
+func DefaultErrorHandler(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
+	code := ErrorCode(err)
+	w.Header().Set("Content-Type", "application/json")
+	w.WriteHeader(code)
+
+	e := jx.GetEncoder()
+	e.ObjStart()
+	e.FieldStart("error_message")
+	e.StrEscape(err.Error())
+	e.ObjEnd()
+
+	_, _ = w.Write(e.Bytes())
+}


Test patch:
diff --git a/examples/ex_test_format/oas_cfg_gen.go b/examples/ex_test_format/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/examples/ex_test_format/oas_cfg_gen.go
+++ b/examples/ex_test_format/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/internal/test_allof/oas_cfg_gen.go b/internal/test_allof/oas_cfg_gen.go
index 5329c60f..87094cf3 100644
--- a/internal/test_allof/oas_cfg_gen.go
+++ b/internal/test_allof/oas_cfg_gen.go
@@ -3,17 +3,14 @@
 package api
 
 import (
-	"context"
 	"net/http"
 	"regexp"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
@@ -23,30 +20,7 @@ var regexMap = map[string]*regexp.Regexp{
 }
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -70,7 +44,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/internal/test_form/oas_cfg_gen.go b/internal/test_form/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/internal/test_form/oas_cfg_gen.go
+++ b/internal/test_form/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/internal/test_http_requests/oas_cfg_gen.go b/internal/test_http_requests/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/internal/test_http_requests/oas_cfg_gen.go
+++ b/internal/test_http_requests/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/internal/test_http_responses/oas_cfg_gen.go b/internal/test_http_responses/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/internal/test_http_responses/oas_cfg_gen.go
+++ b/internal/test_http_responses/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {
diff --git a/internal/test_single_endpoint/oas_cfg_gen.go b/internal/test_single_endpoint/oas_cfg_gen.go
index 7032c644..ea5f5a56 100644
--- a/internal/test_single_endpoint/oas_cfg_gen.go
+++ b/internal/test_single_endpoint/oas_cfg_gen.go
@@ -3,45 +3,19 @@
 package api
 
 import (
-	"context"
 	"net/http"
 
-	"github.com/go-faster/errors"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/metric"
 	"go.opentelemetry.io/otel/trace"
 
 	ht "github.com/ogen-go/ogen/http"
-	"github.com/ogen-go/ogen/json"
 	"github.com/ogen-go/ogen/ogenerrors"
 	"github.com/ogen-go/ogen/otelogen"
 )
 
 // ErrorHandler is error handler.
-type ErrorHandler func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error)
-
-func respondError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
-	var (
-		code    = http.StatusInternalServerError
-		ogenErr ogenerrors.Error
-	)
-	switch {
-	case errors.Is(err, ht.ErrNotImplemented):
-		code = http.StatusNotImplemented
-	case errors.As(err, &ogenErr):
-		code = ogenErr.Code()
-	}
-	w.Header().Set("Content-Type", "application/json")
-	w.WriteHeader(code)
-	data, writeErr := json.Marshal(struct {
-		ErrorMessage string `json:"error_message"`
-	}{
-		ErrorMessage: err.Error(),
-	})
-	if writeErr == nil {
-		w.Write(data)
-	}
-}
+type ErrorHandler = ogenerrors.ErrorHandler
 
 type config struct {
 	TracerProvider     trace.TracerProvider
@@ -65,7 +39,7 @@ func newConfig(opts ...Option) config {
 			w.Header().Set("Allow", allowed)
 			w.WriteHeader(http.StatusMethodNotAllowed)
 		},
-		ErrorHandler:       respondError,
+		ErrorHandler:       ogenerrors.DefaultErrorHandler,
 		MaxMultipartMemory: 32 << 20, // 32 MB
 	}
 	for _, opt := range opts {