gofr-dev/gofr · error

response writer does not support hijacking

Error message

response writer does not support hijacking

What it means

errHijackNotSupported is a sentinel in GoFr's logging middleware returned by StatusResponseWriter.Hijack when the wrapped http.ResponseWriter does not implement http.Hijacker. Connection hijacking (needed for WebSockets and upgraded protocols) is only possible if the underlying writer supports it, so this error surfaces that limitation. It's normally wrapped by the 'cannot hijack connection' error.

Source

Thrown at pkg/gofr/http/middleware/logger.go:19

package middleware

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"net/http"
	"runtime/debug"
	"strings"
	"sync"
	"time"

	"go.opentelemetry.io/otel/trace"
)

var errHijackNotSupported = errors.New("response writer does not support hijacking")

// JSON envelope keys for the panic-recovery error response written by
// panicRecovery. Defined as constants so the same spellings stay
// consistent across the package and the goconst linter is satisfied.
const (
	envelopeCodeKey    = "code"
	envelopeStatusKey  = "status"
	envelopeMessageKey = "message"
)

// StatusResponseWriter Defines own Response Writer to be used for logging of status - as http.ResponseWriter does not let us read status.
type StatusResponseWriter struct {
	http.ResponseWriter
	status int
	// wroteHeader keeps a flag to keep a check that the framework do not attempt to write the header again. This was previously causing
	// `superfluous response.WriteHeader call`. This is particularly helpful in scenarios where the developer has already written header
	// in any custom middlewares.
	wroteHeader bool

View on GitHub (pinned to 187eb24962)

Solutions

  1. Ensure the underlying ResponseWriter implements http.Hijacker (forward Hijack in custom wrappers)
  2. Register WebSocket endpoints so they aren't wrapped by non-hijackable middleware writers
  3. In tests use a real httptest server or a hijacker-capable fake instead of ResponseRecorder
  4. Check for errors.Is(err, middleware.ErrHijackNotSupported) and fall back to a plain connection path

Example fix

// before
type gzipResponseWriter struct{ http.ResponseWriter } // no Hijack -> errHijackNotSupported
// after
type gzipResponseWriter struct{ http.ResponseWriter }
func (w *gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
    if h, ok := w.ResponseWriter.(http.Hijacker); ok { return h.Hijack() }
    return nil, nil, http.ErrNotSupported
}
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := underlying.(http.Hijacker); !ok {
    return errors.New("underlying response writer does not support hijacking; websocket upgrade will fail")
}

Type guard

func supportsHijack(w http.ResponseWriter) bool {
    _, ok := w.(http.Hijacker)
    return ok
}

Try / catch

conn, rw, err := srw.Hijack()
if err != nil {
    if errors.Is(err, middleware.ErrHijackNotSupported) {
        // degrade: respond normally instead of upgrading
        http.Error(srw, "upgrade unsupported", http.StatusInternalServerError)
        return
    }
    return err
}

Prevention

When it happens

Trigger: Calling Hijack() on a StatusResponseWriter whose underlying ResponseWriter lacks a Hijack() method, e.g. when a WebSocket upgrade handler runs through a writer chain (logging, gzip, test fakes) that doesn't implement http.Hijacker.

Common situations: Adding logging or compression middleware above a WebSocket endpoint, using httptest.ResponseRecorder in tests, or custom reverse-proxy writers that don't forward the Hijacker interface.

Related errors


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/5d53d326f05e5964. Report an issue: GitHub.