{"record":{"id":"7c699739726d3fe9","repo":"grpc/grpc-go","slug":"failed-to-send-alts-handshaker-request-w","errorCode":null,"errorMessage":"failed to send ALTS handshaker request: %w","messagePattern":"failed to send ALTS handshaker request: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/alts/internal/handshaker/handshaker.go","lineNumber":305,"sourceCode":"\t// on the returned record protocol.\n\tkeyLen, ok := keyLength[result.RecordProtocol]\n\tif !ok {\n\t\treturn nil, nil, fmt.Errorf(\"unknown resulted record protocol %v\", result.RecordProtocol)\n\t}\n\tmaxFrameSize := int(envconfig.ALTSMaxFrameSize)\n\tif peerMax := int(result.GetMaxFrameSize()); peerMax > 0 {\n\t\tmaxFrameSize = min(peerMax, maxFrameSize)\n\t}\n\tsc, err := conn.NewConnWithMaxFrameSize(h.conn, h.side, result.GetRecordProtocol(), result.KeyData[:keyLen], extra, maxFrameSize)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn sc, result, nil\n}\n\nfunc (h *altsHandshaker) accessHandshakerService(req *altspb.HandshakerReq) (*altspb.HandshakerResp, error) {\n\tif err := h.stream.Send(req); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to send ALTS handshaker request: %w\", err)\n\t}\n\tresp, err := h.stream.Recv()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to receive ALTS handshaker response: %w\", err)\n\t}\n\treturn resp, nil\n}\n\n// processUntilDone processes the handshake until the handshaker service returns\n// the results. Handshaker service takes care of frame parsing, so we read\n// whatever received from the network and send it to the handshaker service.\nfunc (h *altsHandshaker) processUntilDone(resp *altspb.HandshakerResp, extra []byte) (*altspb.HandshakerResult, []byte, error) {\n\tvar lastWriteTime time.Time\n\tbuf := make([]byte, frameLimit)\n\tfor {\n\t\tif len(resp.OutFrames) > 0 {\n\t\t\tlastWriteTime = time.Now()\n\t\t\tif _, err := h.conn.Write(resp.OutFrames); err != nil {","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/alts/internal/handshaker/handshaker.go#L287-L323","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: short context cuts off the handshake\nctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)\nconn, ai, err := h.ClientHandshake(ctx)\n\n// after: deadline generous enough for the ALTS round trips\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\nconn, ai, err := h.ClientHandshake(ctx)","handlingStrategy":"retry","validationCode":"// Ensure the context outlives the Send round-trip.\nfunc handshakeCtx(parent context.Context) (context.Context, context.CancelFunc) {\n    return context.WithTimeout(parent, 5*time.Second)\n}","typeGuard":null,"tryCatchPattern":"_, err := h.accessHandshakerService(req)\nif err != nil {\n    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n        return nil, err // local cancellation; do not retry automatically\n    }\n    // stream broken on send: tear down and re-establish on next handshake.\n    h.Close()\n    return nil, err\n}","preventionTips":["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."],"tags":["alts","grpc","network","stream","handshake"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}