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
- Inspect the inner %v for the cipher-specific cause (key length, AEAD init).
- Ensure both peers use a compatible ALTS record protocol and key derivation.
- 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
- Never hand-construct ALTS keys; let the handshaker produce them.
- Align record protocol and key derivation across both peers.
- Re-run the handshake on transient crypto-init failures.
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
- server-side RPC versions are not compatible with this client
- client-side RPC versions is not compatible with this server,
- received the frame length %d larger than the limit %d
- negotiated unknown next_protocol %q
- received frame with incorrect message type %v, expected lowe
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/4052dafda2418cf4.
Report an issue: GitHub.