{"record":{"id":"0b2281f442437741","repo":"grpc/grpc-go","slug":"received-frame-with-incorrect-message-type-v-exp","errorCode":null,"errorMessage":"received frame with incorrect message type %v, expected lower byte %v","messagePattern":"received frame with incorrect message type (.+?), expected lower byte (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/alts/internal/conn/record.go","lineNumber":273,"sourceCode":"\t\t\t\tnRead, err := p.Conn.Read(protected[len(protected):cap(protected)])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, 0, err\n\t\t\t\t}\n\t\t\t\tprotected = protected[:len(protected)+nRead]\n\t\t\t}\n\t\t\tframedMsg, p.nextFrame, err = ParseFramedMsg(protected, altsRecordLengthLimit)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, 0, err\n\t\t\t}\n\t\t}\n\t\t// Now we have a complete frame, decrypted it.\n\t\tmsg := framedMsg[MsgLenFieldSize:]\n\t\tif len(msg) < msgTypeFieldSize {\n\t\t\treturn nil, 0, fmt.Errorf(\"received frame with size %v which is shorter than message type field size %v\", len(msg), msgTypeFieldSize)\n\t\t}\n\t\tmsgType := binary.LittleEndian.Uint32(msg[:msgTypeFieldSize])\n\t\tif msgType&0xff != altsRecordMsgType {\n\t\t\treturn nil, 0, fmt.Errorf(\"received frame with incorrect message type %v, expected lower byte %v\",\n\t\t\t\tmsgType, altsRecordMsgType)\n\t\t}\n\t\tciphertext := msg[msgTypeFieldSize:]\n\n\t\t// Decrypt directly into the buffer, avoiding a copy from p.buf if\n\t\t// possible.\n\t\tif bufSize >= len(ciphertext) {\n\t\t\tallocatedBuf := pool.Get(bufSize)\n\t\t\tdec, err := p.crypto.Decrypt((*allocatedBuf)[:0], ciphertext)\n\t\t\tif err != nil {\n\t\t\t\tpool.Put(allocatedBuf)\n\t\t\t\treturn nil, 0, err\n\t\t\t}\n\t\t\tp.dropProtectedIfEmtpy()\n\t\t\treturn allocatedBuf, len(dec), nil\n\t\t}\n\t\t// Decrypt requires that if the dst and ciphertext alias, they\n\t\t// must alias exactly. Code here used to use msg[:0], but msg","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/alts/internal/conn/record.go#L255-L291","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: test writes wrong type\nbinary.LittleEndian.PutUint32(msg, 0x99)\n// after\nbinary.LittleEndian.PutUint32(msg, 0x06)  // altsRecordMsgType","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"n, err := altsConn.Read(buf)\nif err != nil {\n    if strings.Contains(err.Error(), \"incorrect message type\") {\n        log.Printf(\"non-ALTS frame on ALTS conn; closing\")\n        altsConn.Close()\n    }\n    return n, err\n}","preventionTips":["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."],"tags":["go","grpc","alts","framing","security"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}