hashicorp/vagrant · error · Vagrant::Errors::UIExpectsTTY

Vagrant is attempting to interface with the UI in a way that

Error message

Vagrant is attempting to interface with the UI in a way that requires
a TTY. Most actions in Vagrant that require a TTY have configuration
switches to disable this requirement. Please do that or run Vagrant
with TTY.

What it means

Raised by Vagrant's interactive UI layer when ui.ask is called and stdin is not a TTY on a non-Windows host (the guard is `raise Errors::UIExpectsTTY if !@stdin.tty? && !Vagrant::Util::Platform.windows?`). Vagrant asks questions (confirmations, credentials) by printing a prompt and reading stdin, which requires a real terminal. The check runs before any input is read, so the process fails fast instead of hanging.

Source

Thrown at lib/vagrant/ui.rb:175

      end

      # Use some light meta-programming to create the various methods to
      # output text to the UI. These all delegate the real functionality
      # to `say`.
      [:detail, :info, :warn, :error, :output, :success].each do |method|
        class_eval <<-CODE
          def #{method}(message, **opts)
            super(message)
            say(#{method.inspect}, message, **opts)
          end
        CODE
      end

      def ask(message, **opts)
        super(message)

        # We can't ask questions when the output isn't a TTY.
        raise Errors::UIExpectsTTY if !@stdin.tty? && !Vagrant::Util::Platform.windows?

        # Setup the options so that the new line is suppressed
        opts ||= {}
        opts[:echo]     = true  if !opts.key?(:echo)
        opts[:new_line] = false if !opts.key?(:new_line)
        opts[:prefix]   = false if !opts.key?(:prefix)

        # Output the data
        say(:info, message, opts)

        input = nil
        if opts[:echo] || !@stdin.respond_to?(:noecho)
          input = @stdin.gets
        else
          begin
            input = @stdin.noecho(&:gets)

            # Output a newline because without echo, the newline isn't

View on GitHub (pinned to 35f3160f4a)

Solutions

  1. Run the command from an interactive terminal or allocate a pseudo-TTY (e.g. `script -qec "vagrant up" /dev/null` or a pty wrapper)
  2. Remove the need for the prompt: pass the non-interactive switch the prompt supports (e.g. `--force`, pre-set credentials) so the ask path is never hit
  3. If you own the calling code, call ui.ask only when stdin is a TTY and use a safe default otherwise
  4. Remember the check cannot fire on Windows; if you see it on Windows tooling, the actual runtime is a non-Windows wrapper (WSL, ssh session)

Example fix

# before (plugin code, fails in CI)
answer = env.ui.ask("Overwrite box? ")

# after
answer = if $stdin.tty?
  env.ui.ask("Overwrite box? ")
else
  opts[:force] ? "y" : raise("non-interactive run requires --force")
end
Defensive patterns

Strategy: validation

Validate before calling

# before calling a prompting API
require 'vagrant/util/platform'

def interactive?
  $stdin.tty? || Vagrant::Util::Platform.windows?
end

answer = interactive? ? env.ui.ask('Overwrite? ') : default_answer

Try / catch

begin
  env.ui.ask('Continue? ')
rescue Vagrant::Errors::UIExpectsTTY
  env.ui.info('Non-interactive run; using default')
  default
end

Prevention

When it happens

Trigger: Any code path calling env.ui.ask / ui.ask (plugin commands, provider or box-add prompts) when Vagrant's stdin is piped, redirected, or detached: `vagrant up < /dev/null`, CI runners, cron, Docker containers, nohup. Windows hosts are explicitly exempt from the check.

Common situations: Running Vagrant in CI (Jenkins/GitLab), inside containers, or from automation where a plugin attempts to prompt; invoking vagrant via a daemon or service wrapper with no controlling terminal.

Related errors


AI-assisted analysis of hashicorp/vagrant@35f3160f4a (2026-08-21). Data as JSON: /api/errors/67ba66ed9d1b1549. Report an issue: GitHub.