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
- Retry the RPC/connection — ALTS handshaker service outages are typically transient; gRPC will reconnect.
- Verify the HandshakerServiceAddress (default metadata.google.internal.:8080) is reachable from the VM: gcurl or a simple TCP test.
- Confirm the workload is actually on GCP (OnGCE true); if not, ALTS is unsupported (you'd normally hit ErrUntrustedPlatform first).
- 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
- Verify the VM is on GCP and the metadata server (metadata.google.internal.:8080) is reachable.
- Monitor handshaker-service stream-establishment failures and alert on sustained rates.
- Keep HandshakerServiceAddress at the default unless you run a custom handshaker service.
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
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- failed to receive ALTS handshaker response
- failed to send ALTS handshaker request
- %v
- client-side RPC versions is not compatible with this…
- server-side RPC versions are not compatible with this…
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)