diff --git a/client/cmd/root.go b/client/cmd/root.go index b599ca859..2ca14c39c 100644 --- a/client/cmd/root.go +++ b/client/cmd/root.go @@ -299,6 +299,14 @@ func DialClientGRPCServer(ctx context.Context, addr string) (*grpc.ClientConn, e // they each carried their own copy of this list — which is how one of them // ended up retrying a refusal the other treated as final. func terminalLoginError(err error) bool { + // A successful Login reaches here with a nil error, and that is not a + // terminal failure. Handled explicitly rather than left to + // gstatus.FromError, which answers (nil, true) for a nil error and leans on + // Status.Code tolerating a nil receiver to come back as codes.OK. + if err == nil { + return false + } + s, ok := gstatus.FromError(err) if !ok { return false diff --git a/client/cmd/up_setconfig_refusal_test.go b/client/cmd/up_setconfig_refusal_test.go index 74623e434..fdf580102 100644 --- a/client/cmd/up_setconfig_refusal_test.go +++ b/client/cmd/up_setconfig_refusal_test.go @@ -74,6 +74,7 @@ func TestTerminalLoginError(t *testing.T) { {name: "daemon unreachable, worth retrying", err: gstatus.Errorf(codes.Unavailable, "connection refused"), wantTerminal: false}, {name: "transient internal failure", err: gstatus.Errorf(codes.Internal, "boom"), wantTerminal: false}, {name: "not a status error", err: errors.New("boom"), wantTerminal: false}, + {name: "no error at all, the login succeeded", err: nil, wantTerminal: false}, } for _, tt := range tests {