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

  1. If you intended to log in, re-run `terraform login <hostname>` and type exactly `yes` at the prompt.
  2. 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.
  3. 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

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


AI-assisted analysis of hashicorp/terraform@c9def3e214 (2026-08-07). Data as JSON: /api/errors/e6f91ea781423da9. Report an issue: GitHub.