grpc/grpc-go · error
received frame with incorrect message type %v, expected lowe
Error message
received frame with incorrect message type %v, expected lower byte %v
What it means
In conn.ReadOnReady (record.go:271-274), after extracting the 4-byte message-type field, its low byte must equal altsRecordMsgType (0x06). A mismatch means the record is not a valid ALTS record and decryption is refused. The check inspects only the low byte to tolerate reserved high bytes, but a wrong low byte is fatal.
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 msgView on GitHub (pinned to 03255a9237)
Solutions
- Verify the peer is a genuine ALTS endpoint using a compatible record protocol.
- Re-establish the handshake; treat a persistent mismatch as a connection-fatal error.
- In tests, write altsRecordMsgType (0x06) into the message-type field when constructing frames.
Example fix
// before: test writes wrong type binary.LittleEndian.PutUint32(msg, 0x99) // after binary.LittleEndian.PutUint32(msg, 0x06) // altsRecordMsgType
Defensive patterns
Strategy: try-catch
Try / catch
n, err := altsConn.Read(buf)
if err != nil {
if strings.Contains(err.Error(), "incorrect message type") {
log.Printf("non-ALTS frame on ALTS conn; closing")
altsConn.Close()
}
return n, err
} Prevention
- Ensure only ALTS traffic reaches the ALTS record layer (correct handshake first).
- Write altsRecordMsgType (0x06) when constructing test frames.
- Treat message-type mismatch as fatal and reconnect.
When it happens
Trigger: A peer sends an ALTS record frame whose message-type field's low byte is not 0x06 — a framing/protocol violation. Surfaced during conn.Read.
Common situations: A non-ALTS or incompatible stream reaching the ALTS record layer; corruption; a handshaker that negotiated a different record format; a fuzz/malformed-frame test.
Related errors
- received the frame length %d larger than the limit %d
- server-side RPC versions are not compatible with this client
- client-side RPC versions is not compatible with this server,
- negotiated unknown next_protocol %q
- protocol %q: %v
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/0b2281f442437741.
Report an issue: GitHub.