{"record":{"id":"1a072e1cf4ad75c5","repo":"grpc/grpc-go","slug":"failed-to-establish-stream-to-alts-handshaker-serv","errorCode":null,"errorMessage":"failed to establish stream to ALTS handshaker service: %v","messagePattern":"failed to establish stream to ALTS handshaker service: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/alts/internal/handshaker/handshaker.go","lineNumber":173,"sourceCode":"\n// ClientHandshake starts and completes a client ALTS handshake for GCP. Once\n// done, ClientHandshake returns a secure connection.\nfunc (h *altsHandshaker) ClientHandshake(ctx context.Context) (net.Conn, credentials.AuthInfo, error) {\n\tif err := clientHandshakes.Acquire(ctx, 1); err != nil {\n\t\treturn nil, nil, err\n\t}\n\tdefer clientHandshakes.Release(1)\n\n\tif h.side != core.ClientSide {\n\t\treturn nil, nil, errors.New(\"only handshakers created using NewClientHandshaker can perform a client handshaker\")\n\t}\n\n\t// TODO(matthewstevenson88): Change unit tests to use public APIs so\n\t// that h.stream can unconditionally be set based on h.clientConn.\n\tif h.stream == nil {\n\t\tstream, err := altsgrpc.NewHandshakerServiceClient(h.clientConn).DoHandshake(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to establish stream to ALTS handshaker service: %v\", err)\n\t\t}\n\t\th.stream = stream\n\t}\n\n\t// Create target identities from service account list.\n\ttargetIdentities := make([]*altspb.Identity, 0, len(h.clientOpts.TargetServiceAccounts))\n\tfor _, account := range h.clientOpts.TargetServiceAccounts {\n\t\ttargetIdentities = append(targetIdentities, &altspb.Identity{\n\t\t\tIdentityOneof: &altspb.Identity_ServiceAccount{\n\t\t\t\tServiceAccount: account,\n\t\t\t},\n\t\t})\n\t}\n\treq := &altspb.HandshakerReq{\n\t\tReqOneof: &altspb.HandshakerReq_ClientStart{\n\t\t\tClientStart: &altspb.StartClientHandshakeReq{\n\t\t\t\tHandshakeSecurityProtocol: hsProtocol,\n\t\t\t\tApplicationProtocols:      appProtocols,","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/credentials/alts/internal/handshaker/handshaker.go#L155-L191","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Quick reachability check before relying on ALTS (informational; not authoritative).\nfunc handshakerReachable(ctx context.Context, addr string) bool {\n    d := net.Dialer{Timeout: 2 * time.Second}\n    c, err := d.DialContext(ctx, \"tcp\", strings.TrimPrefix(addr, \"dns:///\"))\n    if err != nil {\n        return false\n    }\n    c.Close()\n    return true\n}","typeGuard":null,"tryCatchPattern":"// Retry ALTS client dial with backoff; handshaker-service errors are often transient.\nfunc dialWithRetry(ctx context.Context, addr string, creds credentials.TransportCredentials) (*grpc.ClientConn, error) {\n    var conn *grpc.ClientConn\n    var err error\n    for i := 0; i < 3; i++ {\n        conn, err = grpc.Dial(addr, grpc.WithTransportCredentials(creds))\n        if err == nil || !strings.Contains(err.Error(), \"failed to establish stream to ALTS handshaker\") {\n            break\n        }\n        time.Sleep(time.Duration(i+1) * time.Second)\n    }\n    return conn, err\n}","preventionTips":["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."],"tags":["grpc","alts","gcp","handshaker-service","network","transient"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}