grpc/grpc-go · error

unknown resulted record protocol

Error message

unknown resulted record protocol %v

What it means

Returned inside doHandshake when the completed HandshakerResult.RecordProtocol is not present in the local keyLength map (which only contains 'ALTSRP_GCM_AES128_REKEY' -> 44 bytes). Without a known key length, the handshake key cannot be truncated correctly, so the secure connection cannot be finalized.

Solutions

  1. Upgrade grpc-go to a version whose keyLength map includes the negotiated protocol.
  2. If using a custom handshaker service, have it negotiate 'ALTSRP_GCM_AES128_REKEY' for clients/servers built on this grpc-go version.
  3. Inspect HandshakerResult.RecordProtocol in logs to identify the unexpected protocol and file a grpc-go issue if against the standard GCP service.
Defensive patterns

Strategy: try-catch

Try / catch

// Unknown record protocol from handshaker => version skew; fail and upgrade.
if err != nil && strings.Contains(err.Error(), "unknown resulted record protocol") {
    log.Printf("ALTS handshaker returned unsupported record protocol %v; upgrade grpc-go", err)
    return err
}

Prevention

When it happens

Trigger: The GCP handshaker service completes a handshake and returns a result whose RecordProtocol is something other than ALTSRP_GCM_AES128_REKEY. doHandshake looks up keyLength[result.RecordProtocol], fails, and returns this error.

Common situations: Version skew: the GCP handshaker service (or a custom one) negotiated a newer/different record protocol than this grpc-go build understands; a custom handshaker returning a protocol name that isn't registered in the keyLength map; a test mock returning an arbitrary protocol string.

Related errors


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

Appendix: 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 0c51461d27)