hashicorp/terraform · error
operation canceled
Error message
operation canceled
What it means
Returned from `Meta.runOperation` (the operation runner in `meta.go`) when the user sends a **second** interrupt signal (e.g. a second Ctrl+C) after a graceful `Stop()` was already requested. The first signal triggers graceful stop; the second escalates to `FatalInterrupt()` and `op.Cancel()`, then this error is returned after at most a 5-second grace window for the operation to drain.
Solutions
- Send the interrupt only once and wait for graceful stop; the operation should finish or print its own interrupt diagnostics.
- If the operation genuinely hangs past the 5s grace window, file a bug against the provider that is not honoring context cancellation.
- In automation, send a single SIGTERM/SIGINT and rely on Terraform's own graceful shutdown rather than escalating.
Defensive patterns
Strategy: try-catch
Type guard
// Distinguish a hard cancel from a normal operation result:
func isHardCancel(err error) bool {
return err != nil && err.Error() == "operation canceled"
} Try / catch
op, err := meta.runOperation(...)
if err != nil && err.Error() == "operation canceled" {
// user sent a second SIGINT; exit without treating as a Terraform failure
return exitCodeFromFatalInterrupt()
} Prevention
- In automation, send exactly one interrupt and wait for graceful shutdown.
- Do not conflate 'operation canceled' with apply/plan failures in exit-code mapping.
- Surface fatal-interrupt UX distinctly from normal command errors.
When it happens
Trigger: Two signals arrive on `m.ShutdownCh` before `op.Done()` fires: the inner `select` catches the second `<-m.ShutdownCh`, calls `FatalInterrupt` + `Cancel`, waits up to 5s for `op.Done()`, and returns `errors.New("operation canceled")`.
Common situations: User impatiently hits Ctrl+C twice during `terraform apply` / `plan` / `destroy`; long-running provider operations that do not respond quickly to the first interrupt; SIGINT delivered by orchestration that double-sends.
Related errors
- backendDiags.Err()
- Can't ask approval for state migration when interactive…
- can't show a saved cloud plan unless the current root…
- cannot read
- errInteractiveInputDisabled
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/0448f3fdae487320.
Report an issue: GitHub.
Appendix: 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 d32a084675)