{"record":{"id":"1b4d775388ed0064","repo":"grpc/grpc-go","slug":"v","errorCode":null,"errorMessage":"%v","messagePattern":"%v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/alts/internal/handshaker/handshaker.go","lineNumber":271,"sourceCode":"\t}\n\n\tconn, result, err := h.doHandshake(req)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tauthInfo := authinfo.New(result)\n\treturn conn, authInfo, nil\n}\n\nfunc (h *altsHandshaker) doHandshake(req *altspb.HandshakerReq) (net.Conn, *altspb.HandshakerResult, error) {\n\tresp, err := h.accessHandshakerService(req)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\t// Check of the returned status is an error.\n\tif resp.GetStatus() != nil {\n\t\tif got, want := resp.GetStatus().Code, uint32(codes.OK); got != want {\n\t\t\treturn nil, nil, fmt.Errorf(\"%v\", resp.GetStatus().Details)\n\t\t}\n\t}\n\n\tvar extra []byte\n\tif req.GetServerStart() != nil {\n\t\tif resp.GetBytesConsumed() > uint32(len(req.GetServerStart().GetInBytes())) {\n\t\t\treturn nil, nil, errOutOfBound\n\t\t}\n\t\textra = req.GetServerStart().GetInBytes()[resp.GetBytesConsumed():]\n\t}\n\tresult, extra, err := h.processUntilDone(resp, extra)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\t// The handshaker returns a 128 bytes key. It should be truncated based\n\t// on the returned record protocol.\n\tkeyLen, ok := keyLength[result.RecordProtocol]\n\tif !ok {","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/credentials/alts/internal/handshaker/handshaker.go#L253-L289","documentation":"A passthrough error returned from doHandshake when the ALTS handshaker service responded with a non-OK gRPC status. The message is literally %v of resp.GetStatus().Details, so the actionable text comes from the handshaker service itself, not from this library. It means the handshaker accepted the stream but rejected the handshake payload (bad identities, incompatible protocols, authorization failure).","triggerScenarios":"The StartClientHandshakeReq/StartServerHandshakeReq was delivered to the handshaker service but resp.Status.Code != codes.OK at handshaker.go:270. Concrete causes: TargetServiceAccounts in ClientHandshakerOptions do not match any peer identity, RPCVersions are incompatible, the local/peer service account is not authorized, or the record/application protocol negotiation found no overlap.","commonSituations":"Misconfigured target service accounts in ClientHandshakerOptions.TargetServiceAccounts, cross-project calls without IAM authorization, version skew between two ALTS peers (one advertising RpcProtocolVersions the other rejects), or a peer presenting an identity the handshaker service does not trust.","solutions":["Read the inner Details string — it is the only place the real reason appears; match it to the handshaker service's documented failure modes.","Verify ClientHandshakerOptions.TargetServiceAccounts matches the server's actual service account, and that IAM permits the caller.","Align RPCVersions on both peers or omit them to let defaults apply.","Confirm both endpoints are in a trust domain recognized by the ALTS handshaker service."],"exampleFix":"// before: target accounts guessed\nopts := &alts.ClientHandshakerOptions{\n    TargetServiceAccounts: []string{\"wrong@project.iam.gserviceaccount.com\"},\n}\n\n// after: omit to let ALTS derive, or use the verified SA\nopts := &alts.ClientHandshakerOptions{\n    TargetServiceAccounts: []string{\"svc@PROJECT.iam.gserviceaccount.com\"},\n}","handlingStrategy":"try-catch","validationCode":"// Pre-validate handshaker options so the service does not reject them.\nfunc validateClientOpts(o *handshaker.ClientHandshakerOptions) error {\n    for _, sa := range o.TargetServiceAccounts {\n        if !strings.HasSuffix(sa, \".iam.gserviceaccount.com\") {\n            return fmt.Errorf(\"invalid target service account: %q\", sa)\n        }\n    }\n    return nil\n}","typeGuard":"// Narrow a returned handshaker error if it carries gRPC status.\nfunc isHandshakerStatusErr(err error) (string, bool) {\n    var s *status.Status\n    // The inner details are a string; unwrap and inspect.\n    if err != nil && strings.Contains(err.Error(), \"status\") {\n        return err.Error(), true\n    }\n    _ = s\n    return \"\", false\n}","tryCatchPattern":"_, _, err := h.ClientHandshake(ctx)\nif err != nil {\n    // err.Error() is the handshaker service's own Details string; log and surface to the caller.\n    // Do not retry blindly: identity/authz failures are not transient.\n    return fmt.Errorf(\"alts handshake rejected by service: %w\", err)\n}","preventionTips":["Validate TargetServiceAccounts format before constructing ClientHandshakerOptions.","Confirm IAM authorization between caller and target service accounts.","Keep RPCVersions consistent across peers or omit them.","Log the inner Details string — it is the only source of the real reason."],"tags":["alts","grpc","handshake","authorization","identity"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}