hashicorp/terraform · error

Error asking

Error message

Error asking %s: %v

What it means

Inside the confirm() goroutine, op.UIIn.Input returned an error that is neither context.Canceled on the done context nor on stopCtx. The backend reports which input id failed ('override', 'approve', etc.) plus the wrapped error.

Solutions

  1. Provide a real TTY for interactive ops, or use --auto-approve to bypass prompts.
  2. Ensure stdin is not redirected from /dev/null when prompts are expected.
  3. For custom UIIn implementations, make Input return context.Canceled on shutdown rather than a raw error.
  4. Reconnect (e.g. re-attach to tmux/SSH) if the session dropped.
Defensive patterns

Strategy: try-catch

Validate before calling

func interactiveInputAvailable(op *backendrun.Operation) bool {
    return op.UIIn != nil && op.UIOut != nil && isTerminal(os.Stdin)
}
// skip prompts in CI: pass --auto-approve

Type guard

func isTerminal(f *os.File) bool {
    fi, err := f.Stat()
    return err == nil && (fi.Mode()&os.ModeCharDevice) != 0
}

Try / catch

v, err := op.UIIn.Input(ctx, opts)
if err != nil && err != context.Canceled && ctx.Err() != context.Canceled {
    // treat as non-fatal: fall back to default or fail gracefully
    return fallback(opts)
}

Prevention

When it happens

Trigger: op.UIIn.Input(doneCtx, opts) returns non-nil, non-canceled err: stdin closed unexpectedly; terminal read failure; input implementation raised an internal error; EOF from a piped/non-interactive stdin.

Common situations: Running an interactive apply inside CI without a TTY; SSH session dropped mid-prompt; stdin redirected from /dev/null or a closed pipe; custom UIIn implementation threw.

Related errors


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

Appendix: source

Thrown at internal/cloud/backend_common.go:507

					if err == errRunDiscarded {
						err = errApplyDiscarded
						if op.PlanMode == plans.DestroyMode {
							err = errDestroyDiscarded
						}
					}

					result <- err
					return
				}
			}
		}
	}()

	result <- func() error {
		v, err := op.UIIn.Input(doneCtx, opts)
		if err != nil && err != context.Canceled && stopCtx.Err() != context.Canceled {
			return fmt.Errorf("Error asking %s: %v", opts.Id, err)
		}

		// We return the error of our parent channel as we don't
		// care about the error of the doneCtx which is only used
		// within this function. So if the doneCtx was canceled
		// because stopCtx was canceled, this will properly return
		// a context.Canceled error and otherwise it returns nil.
		if doneCtx.Err() == context.Canceled || stopCtx.Err() == context.Canceled {
			return stopCtx.Err()
		}

		// Make sure we cancel the context here so the loop that
		// checks for external changes to the run is ended before
		// we start to make changes ourselves.
		cancel()

		if v != keyword {
			// Retrieve the run again to get its current status.

View on GitHub (pinned to d32a084675)