grpc/grpc-go · error

received the frame length

Error message

received the frame length %d larger than the limit %d

What it means

Returned by conn.ParseFramedMsg during ALTS record reading when the 4-byte little-endian length field of an incoming framed message decodes to a value greater than the configured maximum (maxLen = altsRecordLengthLimit = 1 MiB). This protects the reader from allocating unbounded memory based on an untrusted length header.

Solutions

  1. Verify the peer is a legitimate ALTS endpoint and not corrupted/malicious; check the peer's gRPC version and ALTS implementation.
  2. Capture a packet trace (e.g. tcpdump) of the ALTS connection to inspect the offending frame header.
  3. If you control the peer, ensure it never emits a single ALTS record larger than 1 MiB.
  4. Treat this as a connection-fatal error: tear down the connection and let gRPC reconnect or fail the RPC.
Defensive patterns

Strategy: try-catch

Try / catch

// ALTS framing errors are connection-fatal; let gRPC tear down and reconnect.
// In application RPC code, treat the RPC error with codes.Internal or unavailable:
if st, ok := status.FromErr(err); ok {
    switch st.Code() {
    case codes.Unavailable, codes.Internal:
        // connection broke (possibly oversize frame); retry with backoff
    }
}

Prevention

When it happens

Trigger: An ALTS peer sends (or the stream is corrupted into appearing to send) a frame whose length header claims a size > 1 MiB. ParseFramedMsg is called from conn.ReadOnReady on every read of an ALTS-secured connection.

Common situations: A malicious or buggy peer sending oversized frames; memory corruption / bit-flips on the wire producing a huge length value; an interoperability issue with a non-Go ALTS implementation that frames differently; a man-in-the-middle injecting garbage bytes.

Related errors


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

Appendix: source

Thrown at credentials/alts/internal/conn/common.go:62

	} else {
		head = make([]byte, total)
		copy(head, in)
	}
	tail = head[len(in):]
	return head, tail
}

// ParseFramedMsg parse the provided buffer and returns a frame of the format
// msgLength+msg and any remaining bytes in that buffer.
func ParseFramedMsg(b []byte, maxLen uint32) ([]byte, []byte, error) {
	// If the size field is not complete, return the provided buffer as
	// remaining buffer.
	length, sufficientBytes := parseMessageLength(b)
	if !sufficientBytes {
		return nil, b, nil
	}
	if length > maxLen {
		return nil, nil, fmt.Errorf("received the frame length %d larger than the limit %d", length, maxLen)
	}
	if len(b) < int(length)+4 { // account for the first 4 msg length bytes.
		// Frame is not complete yet.
		return nil, b, nil
	}
	return b[:MsgLenFieldSize+length], b[MsgLenFieldSize+length:], nil
}

// parseMessageLength returns the message length based on frame header. It also
// returns a boolean indicating if the buffer contains sufficient bytes to parse
// the length header. If there are insufficient bytes, (0, false) is returned.
func parseMessageLength(b []byte) (uint32, bool) {
	if len(b) < MsgLenFieldSize {
		return 0, false
	}
	msgLenField := b[:MsgLenFieldSize]
	return binary.LittleEndian.Uint32(msgLenField), true
}

View on GitHub (pinned to 0c51461d27)