grpc/grpc-go · error

failed to establish stream to ALTS handshaker service

Error message

failed to establish stream to ALTS handshaker service: %v

What it means

Returned during an ALTS client handshake (altsHandshaker.ClientHandshake) when the bidirectional stream to the GCP ALTS handshaker service (HandshakerService.DoHandshake) cannot be opened. The wrapped error (%v) is the underlying gRPC stream-creation error. The handshaker service lives at metadata.google.internal.:8080 by default.

Solutions

  1. Retry the RPC/connection — ALTS handshaker service outages are typically transient; gRPC will reconnect.
  2. Verify the HandshakerServiceAddress (default metadata.google.internal.:8080) is reachable from the VM: gcurl or a simple TCP test.
  3. Confirm the workload is actually on GCP (OnGCE true); if not, ALTS is unsupported (you'd normally hit ErrUntrustedPlatform first).
  4. Check GCP status dashboards and metadata-server health; reduce handshake burst concurrency.
Defensive patterns

Strategy: retry

Validate before calling

// Quick reachability check before relying on ALTS (informational; not authoritative).
func handshakerReachable(ctx context.Context, addr string) bool {
    d := net.Dialer{Timeout: 2 * time.Second}
    c, err := d.DialContext(ctx, "tcp", strings.TrimPrefix(addr, "dns:///"))
    if err != nil {
        return false
    }
    c.Close()
    return true
}

Try / catch

// Retry ALTS client dial with backoff; handshaker-service errors are often transient.
func dialWithRetry(ctx context.Context, addr string, creds credentials.TransportCredentials) (*grpc.ClientConn, error) {
    var conn *grpc.ClientConn
    var err error
    for i := 0; i < 3; i++ {
        conn, err = grpc.Dial(addr, grpc.WithTransportCredentials(creds))
        if err == nil || !strings.Contains(err.Error(), "failed to establish stream to ALTS handshaker") {
            break
        }
        time.Sleep(time.Duration(i+1) * time.Second)
    }
    return conn, err
}

Prevention

When it happens

Trigger: On GCP (vmOnGCP==true), NewClientHandshaker succeeded and service.Dial succeeded, but the subsequent DoHandshake(ctx) RPC to open the handshaker stream failed. Reached via alts.ClientHandshake -> chs.ClientHandshake.

Common situations: The metadata server / ALTS handshaker service is temporarily unreachable (network hiccup, metadata service restart, GCE maintenance); a custom HandshakerServiceAddress in ClientOptions pointing at a wrong/down endpoint; the dial to the handshaker service succeeded but the stream RPC itself was rejected (auth, quota); very high handshake concurrency saturating resources.

Understand the failure class

Related errors


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

Appendix: source

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

// ClientHandshake starts and completes a client ALTS handshake for GCP. Once
// done, ClientHandshake returns a secure connection.
func (h *altsHandshaker) ClientHandshake(ctx context.Context) (net.Conn, credentials.AuthInfo, error) {
	if err := clientHandshakes.Acquire(ctx, 1); err != nil {
		return nil, nil, err
	}
	defer clientHandshakes.Release(1)

	if h.side != core.ClientSide {
		return nil, nil, errors.New("only handshakers created using NewClientHandshaker can perform a client handshaker")
	}

	// TODO(matthewstevenson88): Change unit tests to use public APIs so
	// that h.stream can unconditionally be set based on h.clientConn.
	if h.stream == nil {
		stream, err := altsgrpc.NewHandshakerServiceClient(h.clientConn).DoHandshake(ctx)
		if err != nil {
			return nil, nil, fmt.Errorf("failed to establish stream to ALTS handshaker service: %v", err)
		}
		h.stream = stream
	}

	// Create target identities from service account list.
	targetIdentities := make([]*altspb.Identity, 0, len(h.clientOpts.TargetServiceAccounts))
	for _, account := range h.clientOpts.TargetServiceAccounts {
		targetIdentities = append(targetIdentities, &altspb.Identity{
			IdentityOneof: &altspb.Identity_ServiceAccount{
				ServiceAccount: account,
			},
		})
	}
	req := &altspb.HandshakerReq{
		ReqOneof: &altspb.HandshakerReq_ClientStart{
			ClientStart: &altspb.StartClientHandshakeReq{
				HandshakeSecurityProtocol: hsProtocol,
				ApplicationProtocols:      appProtocols,

View on GitHub (pinned to 0c51461d27)