cloudflare/cloudflared · error
status not yet written before attempting to hijack connectio
Error message
status not yet written before attempting to hijack connection
What it means
http2RespWriter implements http.Hijacker. Hijacking (taking over the raw connection, e.g. for websocket upgrades) is only legal after the HTTP status has been written. If Hijack is called while rp.statusWritten is false, it returns `status not yet written before attempting to hijack connection` instead of the connection.
Source
Thrown at connection/http2.go:312
}
func (rp *http2RespWriter) WriteHeader(status int) {
if rp.hijacked() {
rp.log.Warn().Msg("WriteHeader after hijack")
return
}
_ = rp.WriteRespHeaders(status, rp.respHeaders)
}
func (rp *http2RespWriter) hijacked() bool {
rp.hijackedMutex.Lock()
defer rp.hijackedMutex.Unlock()
return rp.hijackedv
}
func (rp *http2RespWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if !rp.statusWritten {
return nil, nil, fmt.Errorf("status not yet written before attempting to hijack connection")
}
// Make sure to flush anything left in the buffer before hijacking
if rp.shouldFlush {
rp.flusher.Flush()
}
rp.hijackedMutex.Lock()
defer rp.hijackedMutex.Unlock()
if rp.hijackedv {
return nil, nil, http.ErrHijacked
}
rp.hijackedv = true
conn := &localProxyConnection{rp}
// We return the http2RespWriter here because we want to make sure that we flush after every write
// otherwise the HTTP2 write buffer waits a few seconds before sending.
readWriter := bufio.NewReadWriter(
bufio.NewReader(rp),
bufio.NewWriter(rp),
)View on GitHub (pinned to 2253eeeb25)
Solutions
- Write the response status (e.g. http.StatusSwitchingProtocols) via WriteHeader before calling Hijack
- Verify the origin's websocket handler completes its 101 response through the writer before hijacking
- Ensure no early return/short-circuit skips the WriteHeader call in the proxy path
- If using a third-party origin library, upgrade it to one that writes status before hijacking
Example fix
// before conn, rw, _ := respWriter.Hijack() // after respWriter.WriteHeader(http.StatusSwitchingProtocols) conn, rw, err := respWriter.Hijack()
Defensive patterns
Strategy: type-guard
Type guard
func canHijack(w http.ResponseWriter) bool {
h, ok := w.(http.Hijacker)
return ok // plus ensure status already written by the caller
} Try / catch
conn, rw, err := respWriter.Hijack()
if err != nil {
// status wasn't written yet; write the 101 then retry once
respWriter.WriteHeader(http.StatusSwitchingProtocols)
conn, rw, err = respWriter.Hijack()
} Prevention
- Always WriteHeader (e.g. 101 Switching Protocols) before Hijack
- Audit proxy handlers for early returns that skip WriteHeader
- Use origin libraries that perform the upgrade handshake in the correct order
When it happens
Trigger: originProxy code (or an origin server library) calls Hijack() on the respWriter before any WriteHeader/Write occurred — typically a websocket or spliced stream handler that skips writing the 101/2xx status first.
Common situations: Origin libraries that hijack immediately on upgrade requests without first writing the switching-protocols response, or custom proxy code reusing cloudflared's respWriter incorrectly.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- internal error: unsupported connection type
- ErrAPINoSuccess
- Failed to fetch page. Server returned: %d
- write to closed websocket connection
- API errors: %s
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/213d78c65d90a93c.
Report an issue: GitHub.