grpc/grpc-go · error

unknown resulted record protocol %v

Error message

unknown resulted record protocol %v

What it means

After a successful ALTS handshake the result's RecordProtocol string is looked up in the local keyLength map (handshaker.go:288), which only knows ALTSRP_GCM_AES128_REKEY. If the handshaker service returned a different protocol name, this error fires. It is a client/server-vs-handshaker-service version incompatibility: the peer or handshaker service negotiated a record protocol this binary's conn package has not registered.

Source

Thrown at credentials/alts/internal/handshaker/handshaker.go:290

		}
	}

	var extra []byte
	if req.GetServerStart() != nil {
		if resp.GetBytesConsumed() > uint32(len(req.GetServerStart().GetInBytes())) {
			return nil, nil, errOutOfBound
		}
		extra = req.GetServerStart().GetInBytes()[resp.GetBytesConsumed():]
	}
	result, extra, err := h.processUntilDone(resp, extra)
	if err != nil {
		return nil, nil, err
	}
	// The handshaker returns a 128 bytes key. It should be truncated based
	// on the returned record protocol.
	keyLen, ok := keyLength[result.RecordProtocol]
	if !ok {
		return nil, nil, fmt.Errorf("unknown resulted record protocol %v", result.RecordProtocol)
	}
	maxFrameSize := int(envconfig.ALTSMaxFrameSize)
	if peerMax := int(result.GetMaxFrameSize()); peerMax > 0 {
		maxFrameSize = min(peerMax, maxFrameSize)
	}
	sc, err := conn.NewConnWithMaxFrameSize(h.conn, h.side, result.GetRecordProtocol(), result.KeyData[:keyLen], extra, maxFrameSize)
	if err != nil {
		return nil, nil, err
	}
	return sc, result, nil
}

func (h *altsHandshaker) accessHandshakerService(req *altspb.HandshakerReq) (*altspb.HandshakerResp, error) {
	if err := h.stream.Send(req); err != nil {
		return nil, fmt.Errorf("failed to send ALTS handshaker request: %w", err)
	}
	resp, err := h.stream.Recv()
	if err != nil {

View on GitHub (pinned to 03255a9237)

Solutions

  1. Upgrade the google.golang.org/grpc module so credentials/alts knows the negotiated record protocol.
  2. Ensure no custom code deregistered rekeyRecordProtocolName or overwrote conn.RegisterProtocol entries.
  3. If running a fork, register the returned protocol name in both altsRecordFuncs and keyLength.
  4. Match the grpc-go version across all peers in the mesh.

Example fix

// before: stale grpc-go
require google.golang.org/grpc v1.50.0

// after: upgrade to a release supporting the handshaker's protocol
require google.golang.org/grpc v1.66.0
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the negotiated record protocol is registered before consuming the result.
func supportedRecordProtocol(name string) bool {
    switch name {
    case "ALTSRP_GCM_AES128_REKEY":
        return true
    }
    return false
}

Try / catch

_, result, err := h.doHandshake(req)
if err != nil && strings.Contains(err.Error(), "unknown resulted record protocol") {
    // version mismatch: upgrade grpc-go; not retryable on this build.
    return fmt.Errorf("alts record protocol unsupported by this grpc-go version: %w", err)
}

Prevention

When it happens

Trigger: doHandshake completes, result.RecordProtocol is not present in keyLength (currently only 'ALTSRP_GCM_AES128_REKEY'). Happens when the handshaker service has been upgraded to negotiate a newer record protocol (or a peer forced one) that the linked grpc-go version's altsRecordFuncs/keyLength map does not contain.

Common situations: Running an outdated grpc-go/credentials/alts module against a newer GCP handshaker service, or a peer pinned to a different ALTS record protocol. Also reproducible in forks that stripped the default protocol registration.

Related errors


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