grpc/grpc-go · error
failed to send ALTS handshaker request: %w
Error message
failed to send ALTS handshaker request: %w
What it means
Returned by accessHandshakerService when h.stream.Send(req) fails while pushing a HandshakerReq onto the bidirectional stream to the ALTS handshaker service. %w preserves the underlying error for errors.Is/errors.As. The stream is broken on the write side: closed, reset, or the context was cancelled.
Source
Thrown at credentials/alts/internal/handshaker/handshaker.go:305
// 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 {
return nil, fmt.Errorf("failed to receive ALTS handshaker response: %w", err)
}
return resp, nil
}
// processUntilDone processes the handshake until the handshaker service returns
// the results. Handshaker service takes care of frame parsing, so we read
// whatever received from the network and send it to the handshaker service.
func (h *altsHandshaker) processUntilDone(resp *altspb.HandshakerResp, extra []byte) (*altspb.HandshakerResult, []byte, error) {
var lastWriteTime time.Time
buf := make([]byte, frameLimit)
for {
if len(resp.OutFrames) > 0 {
lastWriteTime = time.Now()
if _, err := h.conn.Write(resp.OutFrames); err != nil {View on GitHub (pinned to 03255a9237)
Solutions
- Let gRPC's transport retry re-establish the handshaker service ClientConn; ensure the ClientConn passed to NewServerHandshaker/NewClientHandshaker has reconnect enabled.
- Lengthen or remove the per-handshake context deadline so cancellation does not preempt Send.
- Inspect the wrapped error with errors.Is(err, context.Canceled) / context.DeadlineExceeded to distinguish local cancellation from network reset.
- If recurring, check metadata-server connectivity and quota/rate limits on the handshaker service.
Example fix
// before: short context cuts off the handshake ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) conn, ai, err := h.ClientHandshake(ctx) // after: deadline generous enough for the ALTS round trips ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) conn, ai, err := h.ClientHandshake(ctx)
Defensive patterns
Strategy: retry
Validate before calling
// Ensure the context outlives the Send round-trip.
func handshakeCtx(parent context.Context) (context.Context, context.CancelFunc) {
return context.WithTimeout(parent, 5*time.Second)
} Try / catch
_, err := h.accessHandshakerService(req)
if err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
return nil, err // local cancellation; do not retry automatically
}
// stream broken on send: tear down and re-establish on next handshake.
h.Close()
return nil, err
} Prevention
- Use a context deadline generous enough for the full handshake round-trip.
- Tear down and rebuild the handshaker when Send fails instead of looping.
- Rely on the ClientConn's auto-reconnect for the handshaker service stream.
- Watch for context cancellation propagating into the handshake.
When it happens
Trigger: Sending ClientStart/ServerStart/Next handshake messages after the underlying gRPC stream to the handshaker service died — context cancelled (ctx.Done), RST_STREAM from the service, TCP reset, or CloseSend was already called. Fires on every handshake round after the first I/O failure until the stream is re-established.
Common situations: Long-lived connections where the handshaker service stream silently dropped (metadata server churn), process shutdown racing with an in-flight handshake, or the context deadline being shorter than the handshake round-trip time.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- failed to receive ALTS handshaker response: %w
- failed to establish stream to ALTS handshaker service: %v
- %v
- unknown resulted record protocol %v
- xds: CertificateProvider to fetch trusted roots is missing,
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/7c699739726d3fe9.
Report an issue: GitHub.