From d936a99e6bb5954c41eb1caef4ebc542ba09f05b Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Tue, 10 Mar 2026 11:41:05 +0200 Subject: [PATCH] fix(cli): error when CODER_SESSION_TOKEN env var is set during login (#22879) _Disclaimer: created with Opus 4.6 and Coder Agents._ ## Problem When `CODER_SESSION_TOKEN` is set as an environment variable with an invalid value, `coder login` fails with a confusing error: ``` error: Trace=[create api key: ] You are signed out or your session has expired. Please sign in again to continue. Suggestion: Try logging in using 'coder login'. ``` The suggestion to run `coder login` is what the user just did, making it circular and unhelpful. ## Root cause The `--token` flag is mapped to `CODER_SESSION_TOKEN` via serpent. When the env var is set, `coder login` picks it up as the session token and tries to use it to create a new API key, which fails because the token is invalid. Even if login were to succeed and write a new token to disk, subsequent commands would still use the env var (which takes precedence over the on-disk token), so the user would remain stuck. ## Fix Before attempting login, check if `CODER_SESSION_TOKEN` is set in the environment. If so, return a clear error telling the user to unset it: ``` the environment variable CODER_SESSION_TOKEN is set, which takes precedence over the session token stored on disk. Please unset it and try again. unset CODER_SESSION_TOKEN ``` ## Testing Added `TestLogin/SessionTokenEnvVar` that verifies the error is returned when the env var is set. --- cli/login.go | 14 ++++++++++++++ cli/login_test.go | 12 ++++++++++++ 2 files changed, 26 insertions(+) diff --git a/cli/login.go b/cli/login.go index 7fd6a74866..ded67a016c 100644 --- a/cli/login.go +++ b/cli/login.go @@ -356,6 +356,20 @@ func (r *RootCmd) login() *serpent.Command { return nil } + // If CODER_SESSION_TOKEN is set in the environment, abort login. + // The env var takes precedence over a token stored on disk, so + // even if we complete login and write a new token to the session + // file, subsequent CLI commands would still use the environment + // variable value. + if inv.Environ.Get(envSessionToken) != "" { + return xerrors.Errorf( + "%s is set. This environment variable takes precedence over any session token stored on disk.\n\n"+ + "To log in, unset the environment variable and re-run this command:\n\n"+ + "\tunset %s", + envSessionToken, envSessionToken, + ) + } + sessionToken, _ := inv.ParsedFlags().GetString(varToken) if sessionToken == "" { authURL := *serverURL diff --git a/cli/login_test.go b/cli/login_test.go index 5d1af88265..b03354ac5a 100644 --- a/cli/login_test.go +++ b/cli/login_test.go @@ -516,6 +516,18 @@ func TestLogin(t *testing.T) { require.NotEqual(t, client.SessionToken(), sessionFile) }) + t.Run("SessionTokenEnvVar", func(t *testing.T) { + t.Parallel() + client := coderdtest.New(t, nil) + coderdtest.CreateFirstUser(t, client) + root, _ := clitest.New(t, "login", client.URL.String()) + root.Environ.Set("CODER_SESSION_TOKEN", "invalid-token") + err := root.Run() + require.Error(t, err) + require.Contains(t, err.Error(), "CODER_SESSION_TOKEN is set") + require.Contains(t, err.Error(), "unset CODER_SESSION_TOKEN") + }) + t.Run("KeepOrganizationContext", func(t *testing.T) { t.Parallel() client := coderdtest.New(t, nil)