hashicorp/terraform · warning

execution halted

Error message

execution halted

What it means

'execution halted' is returned by the internal stopHook at every lifecycle hook boundary (PreApply, PostDiff, PreProvisionInstance, PreRefresh, etc.) once the stop flag has been set. It is Terraform's cooperative-cancellation signal: an interrupt was requested and the graph walker honors it at the next hook point by returning HookActionHalt plus this error, which surfaces as a halted apply/plan.

Source

Thrown at internal/terraform/hook_stop.go:135

func (h *stopHook) StartAction(id HookActionIdentity) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) ProgressAction(id HookActionIdentity, progress string) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) CompleteAction(id HookActionIdentity, err error) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) PolicyResult(addr string, resp policy.EvaluationResponse) (HookAction, error) {
	return h.hook()
}

func (h *stopHook) hook() (HookAction, error) {
	if h.Stopped() {
		return HookActionHalt, errors.New("execution halted")
	}

	return HookActionContinue, nil
}

// reset should be called within the lock context
func (h *stopHook) Reset() {
	atomic.StoreUint32(&h.stop, 0)
}

func (h *stopHook) Stop() {
	atomic.StoreUint32(&h.stop, 1)
}

func (h *stopHook) Stopped() bool {
	return atomic.LoadUint32(&h.stop) == 1
}

View on GitHub (pinned to c9def3e214)

Solutions

  1. Recognize it as intentional cancellation — inspect whether the user/orchestrator requested the stop; if so, no remediation is needed.
  2. If it happens unexpectedly, audit what is calling Context.Stop() / sending signals to the Terraform process.
  3. For apply, the state has already been updated for resources completed before the halt; rerun `terraform apply` to continue and `terraform state list` to verify what was applied.
  4. Run with -auto-approve disabled and watch for the interrupt; if a hook never returns (stuck provider), the halt may be deferred — see the provider's Close behavior.
Defensive patterns

Strategy: try-catch

Validate before calling

// Embedders: only call Context.Stop() in response to a real signal.
sc := make(chan os.Signal, 1)
signal.Notify(sc, os.Interrupt)
go func() { <-sc; ctx.Stop() }()

Type guard

func isExecutionHalted(err error) bool {
    return err != nil && err.Error() == "execution halted"
}

Try / catch

if _, err := ctx.Apply(); err != nil {
    if isExecutionHalted(err) {
        // user requested stop; state is consistent up to the halt
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Returned at internal/terraform/hook_stop.go:135 inside hook() when h.Stopped() is true (atomic flag stop==1, set by Stop()). The hook() method is invoked by every Pre*/Post* hook method on stopHook, so it fires at the next graph step boundary after Stop() is called.

Common situations: User pressed Ctrl+C (SIGINT) during `terraform apply`/`plan`. A programmatic Stop() was issued by an embedding application or a watchdog. A long-running operation was canceled by an orchestration layer. A second Ctrl+C forces a harder cancel but the first typically yields this halt at the next hook.

Related errors


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