grpc/grpc-go · error

protocol %q: %v

Error message

protocol %q: %v

What it means

NewConnWithMaxFrameSize (record.go:149-151) found the protocol factory but the factory function newCrypto(side, key) returned an error constructing the cipher (e.g. AES-GCM AEAD creation). The error is wrapped as "protocol %q: %v". The key/side passed come from the handshake result.

Source

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

	constPool constBufferPool // stored as a field to avoid heap allocations.
}

// NewConn creates a new secure channel instance given the other party role and
// handshaking result.
func NewConn(c net.Conn, side core.Side, recordProtocol string, key []byte, protected []byte) (net.Conn, error) {
	return NewConnWithMaxFrameSize(c, side, recordProtocol, key, protected, 0)
}

// NewConnWithMaxFrameSize creates a new secure channel instance given the
// other party role, handshaking result, and negotiated maximum frame size.
func NewConnWithMaxFrameSize(c net.Conn, side core.Side, recordProtocol string, key []byte, protected []byte, negotiatedMaxFrameSize int) (net.Conn, error) {
	newCrypto := protocols[recordProtocol]
	if newCrypto == nil {
		return nil, fmt.Errorf("negotiated unknown next_protocol %q", recordProtocol)
	}
	crypto, err := newCrypto(side, key)
	if err != nil {
		return nil, fmt.Errorf("protocol %q: %v", recordProtocol, err)
	}
	overhead := MsgLenFieldSize + msgTypeFieldSize + crypto.EncryptionOverhead()

	// Clamp maxRecordLen to be at least altsRecordDefaultLength.
	maxRecordLen := max(altsRecordDefaultLength, negotiatedMaxFrameSize)
	payloadLengthLimit := maxRecordLen - overhead
	// We pre-allocate protected to be of size 32KB during initialization.
	// We increase the size of the buffer by the required amount if it can't
	// hold a complete encrypted record.
	protectedHandle := readBufPool.Get(max(altsReadBufferInitialSize, len(protected)))
	protectedBuf := *protectedHandle
	// Copy additional data from hanshaker service.
	copy(protectedBuf, protected)
	protectedBuf = protectedBuf[:len(protected)]

	altsConn := &conn{
		Conn:               c,
		reader:             readyreader.New(c),

View on GitHub (pinned to 03255a9237)

Solutions

  1. Inspect the inner %v for the cipher-specific cause (key length, AEAD init).
  2. Ensure both peers use a compatible ALTS record protocol and key derivation.
  3. Re-establish the ALTS handshake; if persistent, align gRPC/ALTS versions on both ends.

Example fix

// before: handshaker returns a 16-byte key for a 32-byte AEAD
// after: align record protocol and key derivation so newCrypto succeeds
// (upgrade gRPC on both sides; do not hand-craft ALTS keys)
Defensive patterns

Strategy: try-catch

Try / catch

c, err := conn.NewConnWithMaxFrameSize(raw, core.ClientSide, rp, key, protected, 0)
if err != nil {
    // inner %v explains the cipher failure (e.g. key length)
    log.Printf("ALTS crypto init failed for %q: %v", rp, err)
    return nil, err
}

Prevention

When it happens

Trigger: The ALTS handshake produced a key that the record protocol's crypto factory rejects — wrong key length, nil key, or an AEAD construction failure for the negotiated protocol (e.g. ALTSRP_GCM).

Common situations: A corrupted or truncated session key from the handshaker service; a custom crypto factory with strict key-length requirements; an interop mismatch producing a malformed key.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/4052dafda2418cf4. Report an issue: GitHub.