{"id":"9863da88294055d9","repo":"labstack/echo","slug":"proxy-raw-hijack-error-w-url-s","errorCode":null,"errorMessage":"proxy raw, hijack error=%w, url=%s","messagePattern":"proxy raw, hijack error=%w, url=(.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"middleware/proxy.go","lineNumber":148,"sourceCode":"func proxyRaw(c *echo.Context, t *ProxyTarget, config ProxyConfig) http.Handler {\n\tvar dialFunc func(ctx context.Context, network, addr string) (net.Conn, error)\n\tif transport, ok := config.Transport.(*http.Transport); ok {\n\t\tif transport.TLSClientConfig != nil {\n\t\t\td := tls.Dialer{\n\t\t\t\tConfig: transport.TLSClientConfig,\n\t\t\t}\n\t\t\tdialFunc = d.DialContext\n\t\t}\n\t}\n\tif dialFunc == nil {\n\t\tvar d net.Dialer\n\t\tdialFunc = d.DialContext\n\t}\n\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tin, _, err := http.NewResponseController(w).Hijack()\n\t\tif err != nil {\n\t\t\tc.Set(\"_error\", fmt.Errorf(\"proxy raw, hijack error=%w, url=%s\", err, t.URL))\n\t\t\treturn\n\t\t}\n\t\tdefer in.Close()\n\n\t\tout, err := dialFunc(c.Request().Context(), \"tcp\", t.URL.Host)\n\t\tif err != nil {\n\t\t\tc.Set(\"_error\", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf(\"proxy raw, dial error=%v, url=%s\", err, t.URL)))\n\t\t\treturn\n\t\t}\n\t\tdefer out.Close()\n\n\t\t// Write header\n\t\terr = r.Write(out)\n\t\tif err != nil {\n\t\t\tc.Set(\"_error\", echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf(\"proxy raw, request header copy error=%v, url=%s\", err, t.URL)))\n\t\t\treturn\n\t\t}\n","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/labstack/echo/blob/05489dc1730161df26b72d1ae2a3ba6fb8178fc7/middleware/proxy.go#L130-L166","documentation":"Stored in the request context (_error) by proxyRaw() when http.NewResponseController(w).Hijack() fails while proxying a WebSocket (or other upgraded) connection. Hijacking takes raw control of the underlying TCP connection; if the ResponseWriter (or its wrapped chain) does not implement http.Hijacker, the ResponseController returns ErrNotSupported and Echo records this error. This is a runtime per-request failure, not a startup error.","triggerScenarios":"A WebSocket request is routed through the Proxy middleware (c.IsWebSocket() is true at proxy.go:394) but the active ResponseWriter does not support hijacking. This happens with custom Response wrappers, test harnesses using httptest.ResponseRecorder, or middleware that swaps in a non-Hijacker writer upstream of the proxy.","commonSituations":"Running proxy tests with httptest without a real hijackable connection; wrapping echo.Response with a custom writer that drops the Hijacker interface; reverse-proxying websockets behind a custom server adapter.","solutions":["Ensure the ResponseWriter chain reaching the proxy implements http.Hijacker (echo.Response does by default).","In tests, use a real http.Server connection or a writer that embeds/forwards Hijack instead of httptest.ResponseRecorder.","Avoid inserting custom middleware that replaces the Response with a non-Hijacker type before the proxy runs."],"exampleFix":"// before: test uses non-hijackable recorder\nrec := httptest.NewRecorder()\nproxy.ServeHTTP(rec, req)\n// after: use a server with a real connection, or a hijackable writer\nsrv := httptest.NewServer(proxy)\ndefer srv.Close()\n// connect a real websocket client to srv.URL","handlingStrategy":"validation","validationCode":"// Ensure the response writer supports hijacking before routing websockets through the proxy.\nfunc canHijack(w http.ResponseWriter) bool {\n    _, _, err := http.NewResponseController(w).Hijack()\n    // We can't actually hijack preemptively; instead check interface satisfaction:\n    _ = err\n    // Prefer an interface check in non-test code:\n    type hijacker interface{ Hijack() (net.Conn, *bufio.ReadWriter, error) }\n    _, ok := w.(hijacker)\n    return ok\n}","typeGuard":null,"tryCatchPattern":"// After proxy.ServeHTTP, check the context-stored error.\nif errVal, ok := c.Get(\"_error\").(error); ok && errVal != nil {\n    if strings.Contains(errVal.Error(), \"hijack error\") {\n        // log and return 502 / downgrade\n    }\n}","preventionTips":["Use httptest.NewServer (real TCP) when testing WebSocket proxying, not ResponseRecorder.","Do not replace echo.Response with a writer that drops the http.Hijacker interface.","Confirm any custom server adapter exposes Hijack before enabling the Proxy middleware."],"tags":["proxy","websocket","hijack","network","runtime"],"analyzedSha":"05489dc1730161df26b72d1ae2a3ba6fb8178fc7","analyzedAt":"2026-08-04T21:32:47.783Z","schemaVersion":2}