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

  1. Let gRPC's transport retry re-establish the handshaker service ClientConn; ensure the ClientConn passed to NewServerHandshaker/NewClientHandshaker has reconnect enabled.
  2. Lengthen or remove the per-handshake context deadline so cancellation does not preempt Send.
  3. Inspect the wrapped error with errors.Is(err, context.Canceled) / context.DeadlineExceeded to distinguish local cancellation from network reset.
  4. 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

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

Related errors


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