ginuerzh/gost · error · net.OpError
write not supported
Error message
write not supported
What it means
http2ServerConn.Write is a stub returning a *net.OpError with "write not supported", because writes must go through the wrapped http.ResponseWriter rather than a raw byte sink. The dummy conn only exposes Close/addr/deadline behavior for interface compatibility.
Source
Thrown at http2.go:894
}
func (c *http2Conn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
// a dummy HTTP2 server conn used by HTTP2 handler
type http2ServerConn struct {
r *http.Request
w http.ResponseWriter
closed chan struct{}
}
func (c *http2ServerConn) Read(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "read", Net: "http2", Source: nil, Addr: nil, Err: errors.New("read not supported")}
}
func (c *http2ServerConn) Write(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "write", Net: "http2", Source: nil, Addr: nil, Err: errors.New("write not supported")}
}
func (c *http2ServerConn) Close() error {
select {
case <-c.closed:
default:
close(c.closed)
}
return nil
}
func (c *http2ServerConn) LocalAddr() net.Addr {
addr, _ := net.ResolveTCPAddr("tcp", c.r.Host)
return addr
}
func (c *http2ServerConn) RemoteAddr() net.Addr {
addr, _ := net.ResolveTCPAddr("tcp", c.r.RemoteAddr)View on GitHub (pinned to a33fdbf4c9)
Solutions
- Write response data via c.w (http.ResponseWriter), after flushing headers with the http.Flusher.
- Use http2Conn from the listener/dialer path for raw conn writes (it backs onto an HTTP/2 stream pipe).
- Replace conn-based relay writes with a pipe: write into an io.Pipe handed to the ResponseWriter.
- Type-check before writing so the dummy server conn is never passed to byte-oriented code.
Example fix
// before
serverConn.Write(buf) // write not supported
// after
serverConn.w.Write(buf) // write via the underlying ResponseWriter
if fw, ok := serverConn.w.(http.Flusher); ok { fw.Flush() } Defensive patterns
Strategy: type-guard
Type guard
func isUnsupportedWrite(err error) bool {
var oe *net.OpError
return errors.As(err, &oe) && oe.Net == "http2" && strings.Contains(err.Error(), "write not supported")
} Try / catch
if _, err := conn.Write(b); err != nil {
if isUnsupportedWrite(err) {
return fmt.Errorf("write via http.ResponseWriter, not the dummy server conn")
}
return err
} Prevention
- Route all response writes through the wrapped http.ResponseWriter and flush headers.
- Do not pass handler-side dummy conns into relay/tunnel write paths.
- Type-assert conns before handing them to byte-oriented helpers.
When it happens
Trigger: Calling Write() on an http2ServerConn, e.g. relaying bytes with io.Copy(serverConn, src) or writing handshake payloads directly to the conn.
Common situations: Treating the handler-side conn as a bidirectional tunnel; porting code from a TCP-based handler where conn.Write worked; forgetting that HTTP/2 response data must be written to the ResponseWriter with proper headers flushed.
Related errors
AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02).
Data as JSON: /api/errors/27faff3043a63a03.
Report an issue: GitHub.