gorilla/websocket · error

websocket: bad write message type

Error message

websocket: bad write message type

What it means

errBadWriteOpCode is returned when WriteMessage is called with an invalid message type — anything other than TextMessage (1), BinaryMessage (2), or an allowed control opcode the write path accepts. The library validates the messageType before writing.

Solutions

  1. Pass only websocket.TextMessage or websocket.BinaryMessage as the first argument to WriteMessage
  2. Check for swapped arguments if using a variable message type
  3. Validate any externally-configured message type against the two allowed constants

Example fix

// before
if err := conn.WriteMessage(msgType, payload); err != nil { ... } // msgType is 0
// after
if msgType != websocket.TextMessage && msgType != websocket.BinaryMessage {
    msgType = websocket.TextMessage
}
if err := conn.WriteMessage(msgType, payload); err != nil { ... }
Defensive patterns

Strategy: validation

Validate before calling

func validMessageType(t int) bool {
    return t == websocket.TextMessage || t == websocket.BinaryMessage
}

Type guard

func isTextOrBinary(t int) bool {
    return t == websocket.TextMessage || t == websocket.BinaryMessage
}

Try / catch

if !validMessageType(msgType) {
    return fmt.Errorf("invalid message type %d", msgType)
}
if err := conn.WriteMessage(msgType, payload); err != nil {
    return err
}

Prevention

When it happens

Trigger: Calling conn.WriteMessage with a messageType constant that is not websocket.TextMessage or websocket.BinaryMessage (e.g. 0, 3-10, or an uninitialized variable), including accidentally passing the message payload's first byte as the type.

Common situations: Passing a dynamic/int messageType computed elsewhere, off-by-one or swapped arguments like WriteMessage(data, websocket.TextMessage), or reusing opcode constants from another WebSocket library.

Related errors


AI-assisted analysis of gorilla/websocket@e064f32e36 (2026-08-31). Data as JSON: /api/errors/b434aaddb6824c27. Report an issue: GitHub.

Appendix: source

Thrown at conn.go:178

// IsUnexpectedCloseError returns boolean indicating whether the error is a
// *CloseError with a code not in the list of expected codes.
func IsUnexpectedCloseError(err error, expectedCodes ...int) bool {
	if e, ok := err.(*CloseError); ok {
		for _, code := range expectedCodes {
			if e.Code == code {
				return false
			}
		}
		return true
	}
	return false
}

var (
	errWriteTimeout        = &netError{msg: "websocket: write timeout", timeout: true, temporary: true}
	errUnexpectedEOF       = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()}
	errBadWriteOpCode      = errors.New("websocket: bad write message type")
	errWriteClosed         = errors.New("websocket: write closed")
	errInvalidControlFrame = errors.New("websocket: invalid control frame")
)

// maskRand is an io.Reader for generating mask bytes. The reader is initialized
// to crypto/rand Reader. Tests swap the reader to a math/rand reader for
// reproducible results.
var maskRand = rand.Reader

// newMaskKey returns a new 32 bit value for masking client frames.
func newMaskKey() [4]byte {
	var k [4]byte
	_, _ = io.ReadFull(maskRand, k[:])
	return k
}

func isControl(frameType int) bool {
	return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage

View on GitHub (pinned to e064f32e36)