hashicorp/terraform · warning

operation canceled

Error message

operation canceled

What it means

Returned by Meta.RunOperation (meta.go:493) when a SECOND interrupt is received. The first signal on m.ShutdownCh triggers a graceful op.Stop(); if the operator hits Ctrl-C again while waiting (line 517 branch), the code calls op.Cancel() for a hard cancel, waits up to 5s, and returns errors.New("operation canceled"). It signals the operation was forcibly terminated rather than completing.

Source

Thrown at internal/command/meta.go:530

		// Notify the user
		opReq.View.Interrupted()

		// Still get the result, since there is still one
		select {
		case <-m.ShutdownCh:
			opReq.View.FatalInterrupt()

			// cancel the operation completely
			op.Cancel()

			// the operation should return asap
			// but timeout just in case
			select {
			case <-op.Done():
			case <-time.After(5 * time.Second):
			}

			return nil, errors.New("operation canceled")

		case <-op.Done():
			// operation completed after Stop
		}
	case <-op.Done():
		// operation completed normally
	}

	return op, nil
}

// contextOpts returns the options to use to initialize a Terraform
// context with the settings from this Meta.
func (m *Meta) contextOpts() (*terraform.ContextOpts, error) {
	workspace, err := m.Workspace()
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to c9def3e214)

Solutions

  1. Press Ctrl-C once and wait for graceful shutdown; only interrupt again if the operation is truly hung.
  2. If a provider routinely ignores Stop, upgrade the provider plugin or check for stuck external API calls.
  3. For automation, send a single SIGINT and poll the process exit rather than double-signaling.

Example fix

# before: double Ctrl-C -> hard cancel, partial state possible
 ^C^C
# after: single interrupt, let graceful Stop complete
 ^C   # wait for 'Interrupt received' then clean exit
Defensive patterns

Strategy: try-catch

Validate before calling

// No pre-validation prevents a double-SIGINT; instead structure signal handling.
 sigCh := make(chan os.Signal, 1)
 signal.Notify(sigCh, os.Interrupt)

Try / catch

op, err := meta.RunOperation(b, opReq)
 if err != nil && err.Error() == "operation canceled" {
     // double-interrupt hard-cancel; surface partial-state risk to the operator
     log.Println("operation forcibly cancelled; verify state consistency")
 }

Prevention

When it happens

Trigger: Pressing Ctrl-C twice (or sending SIGINT twice) during a long-running `terraform apply`, `terraform plan`, `terraform destroy`, or `terraform refresh` — the first initiates graceful shutdown, the second forces cancellation.

Common situations: Operator impatient during a slow apply and mashing Ctrl-C; CI runner sending SIGINT twice on timeout/job cancel; a hung provider that ignores graceful Stop, prompting a second interrupt.

Related errors


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