grpc/grpc-go · error

received frame with incorrect message type

Error message

received frame with incorrect message type %v, expected lower byte %v

What it means

Returned during ALTS record reading when the 4-byte little-endian message type field's lowest byte does not equal altsRecordMsgType (0x06). The ALTS record format mandates a message type whose low byte is 0x06; a mismatch means the data on the wire is not a valid ALTS record.

Solutions

  1. Confirm both endpoints are using ALTS transport credentials (not mixing ALTS with TLS or insecure).
  2. Check for a proxy, sidecar, or load balancer that may be speaking a different protocol on the wire.
  3. Capture a packet trace to confirm whether the bytes are valid ALTS records.
  4. Close the connection; it cannot recover once framing is desynchronized.
Defensive patterns

Strategy: try-catch

Try / catch

// Wrong message type => not ALTS data on this connection; treat as fatal.
if err != nil && strings.Contains(err.Error(), "incorrect message type") {
    log.Printf("non-ALTS data on ALTS connection: %v", err)
    return err // let gRPC reconnect or fail the RPC
}

Prevention

When it happens

Trigger: A frame passes the length checks but its message-type byte differs from 0x06. Reached on every read of an ALTS connection after a complete frame is assembled.

Common situations: Non-ALTS data arriving on a connection that was supposed to be ALTS-secured (e.g. plaintext HTTP/2 or a different protocol); corruption of the message-type bytes; an interoperability mismatch with a peer using a different ALTS record format version.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/0b2281f442437741. Report an issue: GitHub.

Appendix: source

Thrown at credentials/alts/internal/conn/record.go:273

				nRead, err := p.Conn.Read(protected[len(protected):cap(protected)])
				if err != nil {
					return nil, 0, err
				}
				protected = protected[:len(protected)+nRead]
			}
			framedMsg, p.nextFrame, err = ParseFramedMsg(protected, altsRecordLengthLimit)
			if err != nil {
				return nil, 0, err
			}
		}
		// Now we have a complete frame, decrypted it.
		msg := framedMsg[MsgLenFieldSize:]
		if len(msg) < msgTypeFieldSize {
			return nil, 0, fmt.Errorf("received frame with size %v which is shorter than message type field size %v", len(msg), msgTypeFieldSize)
		}
		msgType := binary.LittleEndian.Uint32(msg[:msgTypeFieldSize])
		if msgType&0xff != altsRecordMsgType {
			return nil, 0, fmt.Errorf("received frame with incorrect message type %v, expected lower byte %v",
				msgType, altsRecordMsgType)
		}
		ciphertext := msg[msgTypeFieldSize:]

		// Decrypt directly into the buffer, avoiding a copy from p.buf if
		// possible.
		if bufSize >= len(ciphertext) {
			allocatedBuf := pool.Get(bufSize)
			dec, err := p.crypto.Decrypt((*allocatedBuf)[:0], ciphertext)
			if err != nil {
				pool.Put(allocatedBuf)
				return nil, 0, err
			}
			p.dropProtectedIfEmtpy()
			return allocatedBuf, len(dec), nil
		}
		// Decrypt requires that if the dst and ciphertext alias, they
		// must alias exactly. Code here used to use msg[:0], but msg

View on GitHub (pinned to 0c51461d27)