gin-gonic/gin · error · errHijackAlreadyWritten

gin: response body already written

Error message

gin: response body already written

What it means

errHijackAlreadyWritten is returned by ResponseWriter.Hijack (response_writer.go:115) when Hijack is called after response body bytes (size > 0) have already been written. Once body data is on the wire the connection can no longer be upgraded/hijacked cleanly; Gin permits hijack only before body writes (size == -1) or right after headers (size == 0).

Source

Thrown at response_writer.go:20

// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.

package gin

import (
	"bufio"
	"errors"
	"io"
	"net"
	"net/http"
)

const (
	noWritten     = -1
	defaultStatus = http.StatusOK
)

var errHijackAlreadyWritten = errors.New("gin: response body already written")

// ResponseWriter ...
type ResponseWriter interface {
	http.ResponseWriter
	http.Hijacker
	http.Flusher
	http.CloseNotifier

	// Status returns the HTTP response status code of the current request.
	Status() int

	// Size returns the number of bytes already written into the response http body.
	// See Written()
	Size() int

	// WriteString writes the string into the response body.
	WriteString(string) (int, error)

View on GitHub (pinned to 34dac209ff)

Solutions

  1. Ensure no body bytes are written before calling Hijack; branch the handler so the upgrade path never writes a normal response.
  2. Move the WebSocket upgrade to the very first thing the handler does, before any c.JSON / c.String.
  3. If you conditionally write, call c.Writer.Written() / c.Writer.Size() and skip writing before attempting Hijack.

Example fix

// before
c.String(http.StatusOK, "hi")
conn, _, _ := c.Writer.Hijack() // error
// after
conn, buf, err := c.Writer.Hijack()
if err != nil { /* handle */ return }
defer conn.Close()
// do websocket handshake on conn
Defensive patterns

Strategy: validation

Validate before calling

if c.Writer.Written() || c.Writer.Size() > 0 {
    return errors.New("cannot hijack after body written")
}
conn, buf, err := c.Writer.Hijack()

Try / catch

conn, buf, err := c.Writer.Hijack()
if err != nil {
    if errors.Is(err, errHijackAlreadyWritten) {
        // response already started; cannot upgrade
    }
}

Prevention

When it happens

Trigger: Calling c.Writer.Hijack() after a c.JSON / c.String / c.Write that wrote body bytes; WebSocket upgrade libraries (e.g. github.com/coder/websocket) attempting to upgrade after the handler wrote a partial response; SSE handlers that wrote an event then try to upgrade.

Common situations: Mixing a normal HTTP response with a WebSocket upgrade in the same handler; flushing headers + partial body then calling Hijack; middleware that writes an error before delegating to a websocket upgrader.

Related errors


AI-assisted analysis of gin-gonic/gin@34dac209ff (2026-08-04). Data as JSON: /data/errors/3bb45247e1350c9a.json. Report an issue: GitHub.