labstack/echo · error
ResponseWriter does not implement 'Unwrap() http.ResponseWri
Error message
ResponseWriter does not implement 'Unwrap() http.ResponseWriter' interface or unwrap to *echo.Response
What it means
Returned by echo.UnwrapResponse when the given http.ResponseWriter cannot be unwrapped down to a *echo.Response. UnwrapResponse walks the Unwrap() chain (each wrapper must implement Unwrap() http.ResponseWriter) looking for a *Response; if it hits a writer that has neither type it gives up. This helper is used internally (e.g., by the HEAD-request writer and error-handling path) but can be called by user middleware too.
Source
Thrown at response.go:130
r.afterFuncs = nil
r.ResponseWriter = w
r.Size = 0
r.Status = http.StatusOK
r.Committed = false
}
// UnwrapResponse unwraps given ResponseWriter to return contexts original Echo Response. rw has to implement
// following method `Unwrap() http.ResponseWriter`
func UnwrapResponse(rw http.ResponseWriter) (*Response, error) {
for {
switch t := rw.(type) {
case *Response:
return t, nil
case interface{ Unwrap() http.ResponseWriter }:
rw = t.Unwrap()
continue
default:
return nil, errors.New("ResponseWriter does not implement 'Unwrap() http.ResponseWriter' interface or unwrap to *echo.Response")
}
}
}
// delayedStatusWriter is a wrapper around http.ResponseWriter that delays writing the status code until first Write is called.
// This allows (global) error handler to decide correct status code to be sent to the client.
type delayedStatusWriter struct {
http.ResponseWriter
committed bool
status int
}
func (w *delayedStatusWriter) WriteHeader(statusCode int) {
// in case something else writes status code explicitly before us we need mark response committed
w.committed = true
w.ResponseWriter.WriteHeader(statusCode)
}
View on GitHub (pinned to 05489dc173)
Solutions
- Make any custom http.ResponseWriter wrapper implement Unwrap() http.ResponseWriter returning the underlying writer, so the chain reaches *echo.Response.
- If you only need the *echo.Response, access it via c.Response() from the Context rather than unwrapping an arbitrary writer.
- Check the returned error from UnwrapResponse before using the *Response (it is a (resp, err) signature).
- When testing, wrap httptest.NewRecorder() in an echo.Response via echo.NewResponse(rec, logger) instead of passing the bare recorder.
Example fix
// before: custom wrapper breaks the unwrap chain
type myWriter struct{ http.ResponseWriter }
// after: implement Unwrap so ResponseController / UnwrapResponse work
type myWriter struct{ http.ResponseWriter }
func (w *myWriter) Unwrap() http.ResponseWriter { return w.ResponseWriter } Defensive patterns
Strategy: type-guard
Type guard
func isUnwrappable(rw http.ResponseWriter) bool {
_, err := echo.UnwrapResponse(rw)
return err == nil
}
// or check the interface directly:
func hasUnwrap(rw http.ResponseWriter) bool {
_, ok := rw.(interface{ Unwrap() http.ResponseWriter })
if !ok {
_, ok = rw.(*echo.Response)
}
return ok
} Prevention
- Implement Unwrap() http.ResponseWriter on any custom ResponseWriter wrapper so the chain reaches *echo.Response.
- Prefer c.Response() over manually unwrapping writers when you just need the Echo response.
- In tests, wrap httptest.NewRecorder() with echo.NewResponse(rec, logger) rather than passing the bare recorder.
- Always check the error returned by echo.UnwrapResponse before using the result.
When it happens
Trigger: Passing an http.ResponseWriter obtained outside Echo's request lifecycle, or a custom wrapper that does not implement Unwrap() (or whose Unwrap chain never reaches *echo.Response) to echo.UnwrapResponse.
Common situations: User middleware replaces c.Response() with a custom writer that does not implement Unwrap(); testing with httptest.NewRecorder() directly against code that calls UnwrapResponse; or a third-party wrapper that fails to forward Unwrap.
Related errors
- response writer flushing is not supported
- route not found
- route not found by method and path
- route not found by method
- echo: response writer %T does not support flushing (http.Flu
AI-assisted analysis of labstack/echo@05489dc173 (2026-08-04).
Data as JSON: /data/errors/95bef54f59b14171.json.
Report an issue: GitHub.