hashicorp/terraform · warning
Login cancelled
Error message
Login cancelled
What it means
Returned by `terraform login` when the user answers "no" to the OAuth authorization-code consent prompt shown by `interactiveContextConsent` before the code-grant flow begins. It is a deliberate user abort, not a library failure: the command appends it as a diagnostic and returns no token.
Solutions
- Re-run `terraform login <hostname>` and answer "yes" at the consent prompt to proceed with the OAuth code grant.
- If running non-interactively, set the credentials directly in ~/.terraform.d/credentials.tfrc.json or supply a TF_TOKEN_<hostname> environment variable instead of using the interactive flow.
- Verify the hostname argument matches a host that actually serves `terraform-login` disco metadata.
Example fix
// before: terraform login app.terraform.io -> user types 'no' // after: terraform login app.terraform.io -> user types 'yes' at consent
Defensive patterns
Strategy: validation
Validate before calling
// Before invoking the login flow, gate on input capability:
if !cmd.Input() {
return errors.New("cannot perform interactive OAuth login with input disabled; set TF_TOKEN_<host> instead")
} Prevention
- Do not run `terraform login` under `-input=false`; it always requires a consent prompt.
- For automation, supply credentials via TF_TOKEN_<hostname> or credentials.tfrc.json rather than the interactive flow.
- If you wrap Terraform, treat 'Login cancelled' as a non-retryable user decision, not a transient error.
When it happens
Trigger: `LoginCommand.interactiveGetTokenByCode` runs for a host whose disco metadata advertises an OAuth authorization-code grant; `interactiveContextConsent` returns `confirm=false` because the user typed a negative answer at the consent prompt.
Common situations: Running `terraform login app.terraform.io` (or a private TFE hostname) in a terminal and selecting "no" / typing something other than yes when asked to approve the authorization request; CI shells where stdin returns an empty or non-affirmative answer.
Related errors
- Can't ask approval for state migration when interactive…
- errInteractiveInputDisabled
- Failed to request password
- Failed to request username
- Failed to retrieve token
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/e6f91ea781423da9.
Report an issue: GitHub.
Appendix: 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 d32a084675)