grpc/grpc-go · error
negotiated unknown next_protocol %q
Error message
negotiated unknown next_protocol %q
What it means
NewConnWithMaxFrameSize (record.go:144-148) looks up the negotiated record protocol name in the protocols map to obtain its crypto factory. If the handshake selected a next_protocol that has no registered factory, the secure connection cannot be created. The %q is the unknown protocol name returned by the ALTS handshaker.
Source
Thrown at credentials/alts/internal/conn/record.go:147
// nextFrame stores the next frame (in protected buffer) info.
nextFrame []byte
// overhead is the calculated overhead of each frame.
overhead int
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)]View on GitHub (pinned to 03255a9237)
Solutions
- Update the gRPC/ALTS build so the negotiated protocol's factory is registered.
- If custom, register your record protocol via conn.RegisterProtocol before handshaking.
- Align client and handshaker-service versions so they negotiate a mutually known protocol.
Example fix
// before: build missing ALTSRP_GCM registration // after import _ "google.golang.org/grpc/credentials/alts/internal/conn" // ensures init() registers protocols // or upgrade the gRPC module to a version that knows the negotiated protocol
Defensive patterns
Strategy: try-catch
Try / catch
c, err := conn.NewConnWithMaxFrameSize(raw, core.ClientSide, rp, key, protected, 0)
if err != nil {
if strings.Contains(err.Error(), "negotiated unknown next_protocol") {
log.Printf("ALTS negotiated an unsupported protocol %q; upgrade gRPC", rp)
}
return nil, err
} Prevention
- Keep client and GCP handshaker service on compatible gRPC versions.
- Register any custom record protocol before handshaking.
- Log the negotiated protocol name for fast diagnosis.
When it happens
Trigger: The ALTS handshake negotiates a record protocol (e.g. a newer ALTSRP_*) that this build of the conn package didn't register. Arises from version skew between the handshaker service result and the compiled-in crypto protocols.
Common situations: A newer handshaker service advertises a protocol the older client lib doesn't know; a custom/forked build missing an init() registration; mismatched gRPC versions across client and GCP handshaker.
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
- protocol %q: %v
- 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/e8cbb2e9661c7ec4.
Report an issue: GitHub.