{"record":{"id":"f4b47a2af7096178","repo":"grpc/grpc-go","slug":"received-frame-with-size-v-which-is-shorter-than","errorCode":null,"errorMessage":"received frame with size %v which is shorter than message type field size %v","messagePattern":"received frame with size (.+?) which is shorter than message type field size (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/alts/internal/conn/record.go","lineNumber":269,"sourceCode":"\t\t\t\t}\n\t\t\t\tp.protectedHandle = newBuf\n\t\t\t\tprotected = (*newBuf)[:nRead]\n\t\t\t} else {\n\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()","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/alts/internal/conn/record.go#L251-L287","documentation":"In conn.ReadOnReady (record.go:267-269), after a complete frame is parsed, the payload after the 4-byte length header must still contain the 4-byte message-type field (msgTypeFieldSize). If the declared frame length is so small that less than 4 bytes of payload remain, the frame is malformed and the connection errors. This is a defensive check on ALTS record framing.","triggerScenarios":"A peer (or corrupted stream) sends an ALTS record whose length header indicates a payload smaller than the 4-byte message-type sub-field — e.g. a frame length of 0–3. Encountered during conn.Read/ReadOnReady.","commonSituations":"A non-conformant or malicious peer; stream corruption; a fuzz test feeding truncated frames; an ALTS implementation bug producing zero-length records.","solutions":["Ensure the peer implements ALTS record framing correctly (length covers >= msgTypeFieldSize).","Treat as connection-fatal: close the ALTS connection and reconnect.","In tests, build frames with at least MsgLenFieldSize+msgTypeFieldSize bytes."],"exampleFix":"// before: test frame declares length 2\nbinary.LittleEndian.PutUint32(buf, 2)\n// after: declare length >= 4 (msgTypeFieldSize)\nbinary.LittleEndian.PutUint32(buf, 4)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"n, err := altsConn.Read(buf)\nif err != nil {\n    if strings.Contains(err.Error(), \"shorter than message type field size\") {\n        log.Printf(\"malformed ALTS frame; closing connection\")\n        altsConn.Close()\n    }\n    return n, err\n}","preventionTips":["Treat malformed-frame errors as connection-fatal.","In tests, always include the msgTypeFieldSize (4 bytes) of payload.","Fuzz-test peers to confirm they reject truncated frames."],"tags":["go","grpc","alts","framing","validation"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}