hashicorp/terraform · info
Login cancelled
Error message
Login cancelled
What it means
Thrown by `terraform login` during the OAuth authorization-code grant flow (login.go:367 interactiveGetTokenByCode). Before starting the OAuth callback server the command calls interactiveContextConsent() which prompts "Do you want to proceed?"; only the literal answer "yes" confirms. Anything else returns confirm=false, and line 373 appends this plain error to the diagnostics. It is a deliberate user-initiated cancellation, not a system failure.
Source
Thrown at internal/command/login.go:373
// Synopsis implements cli.Command.
func (c *LoginCommand) Synopsis() string {
return "Obtain and save credentials for a remote host"
}
func (c *LoginCommand) defaultOutputFile() string {
if c.CLIConfigDir == "" {
return "" // no default available
}
return filepath.Join(c.CLIConfigDir, "credentials.tfrc.json")
}
func (c *LoginCommand) interactiveGetTokenByCode(hostname svchost.Hostname, credsCtx *loginCredentialsContext, clientConfig *disco.OAuthClient) (*oauth2.Token, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
confirm, confirmDiags := c.interactiveContextConsent(hostname, disco.OAuthAuthzCodeGrant, credsCtx)
diags = diags.Append(confirmDiags)
if !confirm {
diags = diags.Append(errors.New("Login cancelled"))
return nil, diags
}
// We'll use an entirely pseudo-random UUID for our temporary request
// state. The OAuth server must echo this back to us in the callback
// request to make it difficult for some other running process to
// interfere by sending its own request to our temporary server.
reqState, err := uuid.GenerateUUID()
if err != nil {
// This should be very unlikely, but could potentially occur if e.g.
// there's not enough pseudo-random entropy available.
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Can't generate login request state",
fmt.Sprintf("Cannot generate random request identifier for login request: %s.", err),
))
return nil, diags
}View on GitHub (pinned to c9def3e214)
Solutions
- If you intended to log in, re-run `terraform login <hostname>` and type exactly `yes` at the prompt.
- If running in CI/non-interactive, skip `terraform login` and instead set a token via the TF_TOKEN_<hostname> environment variable or write a credentials block in ~/.terraform.d/credentials.tfrc.json.
- If you wanted to cancel, ignore the message — no credentials were written.
Example fix
# before (non-interactive, fails) terraform login app.terraform.io # after (set token directly, no prompt) export TF_TOKEN_app_terraform_io="<token>" terraform init
Defensive patterns
Strategy: validation
Validate before calling
// Before launching `terraform login`, check whether a prompt is even possible.
if !isInteractive(stdin) || tokenAlreadyConfigured(hostname) {
// skip login, set TF_TOKEN_<hostname> or credentials file instead
return
} Try / catch
// When shelling out to terraform login, expect exit!=0 on user cancel and do not treat it as a hard failure.
out, err := exec.Command("terraform", "login", host).CombinedOutput()
if err != nil && bytes.Contains(out, []byte("Login cancelled")) {
log.Println("login declined by user/operator")
return
} Prevention
- Pre-configure tokens via TF_TOKEN_<hostname> env vars or credentials.tfrc.json to avoid the interactive login flow entirely in automation.
- When scripting, supply the consent answer only when stdin is a TTY; otherwise fail fast before launching login.
When it happens
Trigger: Running `terraform login <hostname>` against a host whose service discovery advertises an OAuth authorization-code grant, then answering the consent prompt with anything other than "yes" ("no", empty enter, a typo, or EOF on stdin).
Common situations: Operator runs `terraform login app.terraform.io` by mistake and answers "no" to abort; non-interactive shell where stdin closes (EOF) so the empty default is not "yes"; piped input that supplies "Yes" with a capital Y (strings.ToLower handles this) vs a stray space.
Related errors
- Failed to request username: %s
- Failed to request password: %s
- Failed to retrieve token: %s
- no suitable TCP ports (between %d and %d) are available for
- interrupted
AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07).
Data as JSON: /api/errors/e6f91ea781423da9.
Report an issue: GitHub.