hashicorp/terraform · error

error asking for approval

Error message

error asking for approval: %w

What it means

Thrown by the local backend's opApply when op.UIIn.Input returns an error while prompting the user to type 'yes' to approve an apply. It does NOT fire when the user types a wrong answer (that path sets OperationFailure); it fires only when the input mechanism itself fails.

Solutions

  1. Run non-interactive applies with -input=false and -auto-approve (or supply a plan file) so the prompt is never issued.
  2. If interactive, ensure stdin is a TTY and is not being closed prematurely (no `</dev/null`, no truncated pipe).
  3. Trap interrupts at the caller and treat context.Canceled on stopCtx as a clean cancellation rather than an error.
  4. Check the wrapped err: if it is io.EOF or context.Canceled, surface a clearer 'apply cancelled' message instead of a raw error.

Example fix

# before: piped input closes early, causing 'error asking for approval'
echo yes | terraform apply
# after: explicit non-interactive approval
terraform apply -auto-approve
Defensive patterns

Strategy: try-catch

Validate before calling

// Decide whether to even show the approval prompt.
func shouldPrompt(op *backendrun.Operation) bool {
    return op.UIIn != nil && op.AutoApprove == false && isInteractive(os.Stdin)
}
func isInteractive(f *os.File) bool {
    fi, err := f.Stat()
    return err == nil && (fi.Mode()&os.ModeCharDevice) != 0
}

Type guard

null

Try / catch

v, err := op.UIIn.Input(stopCtx, opts)
if err != nil {
    if errors.Is(err, context.Canceled) || errors.Is(err, io.EOF) {
        // user-driven cancellation: report as cancelled, not as a hard error
        op.View.Cancelled(op.PlanMode)
        runningOp.Result = backendrun.OperationFailure
        return
    }
    diags = diags.Append(fmt.Errorf("error asking for approval: %w", err))
    op.ReportResult(runningOp, diags)
    return
}

Prevention

When it happens

Trigger: op.UIIn.Input(stopCtx, &terraform.InputOpts{Id:"approve",...}) returns a non-nil error. Common causes: stdin closed (EOF), the stop context was cancelled (Ctrl-C / interrupt), the terminal read failed, or -input was not disabled in a non-interactive environment that still routed through UIIn.

Common situations: Piping a plan into `terraform apply -` and the pipe closes before the prompt; running apply in CI without `-auto-approve` and without `-input=false`; user pressed Ctrl-C exactly as the prompt was shown; SSH session dropped mid-apply.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/4b863fb7352db973. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/local/backend_apply.go:190

				}
				desc = "Terraform will perform the actions described above.\n" +
					"Only 'yes' will be accepted to approve."
			}

			// We'll show any accumulated warnings before we display the prompt,
			// so the user can consider them when deciding how to answer.
			if len(diags) > 0 {
				op.View.Diagnostics(diags)
				diags = nil // reset so we won't show the same diagnostics again later
			}

			v, err := op.UIIn.Input(stopCtx, &terraform.InputOpts{
				Id:          "approve",
				Query:       "\n" + query,
				Description: desc,
			})
			if err != nil {
				diags = diags.Append(fmt.Errorf("error asking for approval: %w", err))
				op.ReportResult(runningOp, diags)
				return
			}
			if v != "yes" {
				op.View.Cancelled(op.PlanMode)
				runningOp.Result = backendrun.OperationFailure
				return
			}
		} else {
			// If we didn't ask for confirmation from the user, and they have
			// included any failing checks in their configuration, then they
			// will see a very confusing output after the apply operation
			// completes. This is because all the diagnostics from the plan
			// operation will now be shown alongside the diagnostics from the
			// apply operation. For check diagnostics, the plan output is
			// irrelevant and simple noise after the same set of checks have
			// been executed again during the apply stage. As such, we are going
			// to remove all diagnostics marked as check diagnostics at this

View on GitHub (pinned to d32a084675)