hashicorp/vagrant · info · Vagrant::Errors::VagrantInterrupt

Vagrant exited after cleanup due to external interrupt.

Error message

Vagrant exited after cleanup due to external interrupt.

What it means

VagrantInterrupt is raised by Action::Warden#call at lib/vagrant/action/warden.rb:35 before the next middleware is dispatched when env[:interrupted] is truthy. The flag is set by Action::Runner's signal handler after the process receives SIGINT (Ctrl-C). Raising here lets the warden's rescue block run each middleware's recover handler, so cleanup executes before the process exits with this message.

Source

Thrown at lib/vagrant/action/warden.rb:35

    # interest except to those who are curious about the internal workings
    # of Vagrant.
    class Warden
      attr_accessor :actions, :stack

      def initialize(actions, env)
        @stack      = []
        @actions    = actions.map { |m| finalize_action(m, env) }.flatten
        @logger     = Log4r::Logger.new("vagrant::action::warden")
        @last_error = nil
      end

      def call(env)
        return if @actions.empty?

        begin
          # Call the next middleware in the sequence, appending to the stack
          # of "recoverable" middlewares in case something goes wrong!
          raise Errors::VagrantInterrupt if env[:interrupted]
          action = @actions.shift
          @logger.info("Calling IN action: #{action}")
          @stack.unshift(action).first.call(env)
          raise Errors::VagrantInterrupt if env[:interrupted]
          @logger.info("Calling OUT action: #{action}")
        rescue SystemExit, NoMemoryError
          # This means that an "exit" or "abort" was called, or we have run out
          # of memory. In these cases, we just exit immediately.
          raise
        rescue Exception => e
          # We guard this so that the Warden only outputs this once for
          # an exception that bubbles up.
          if e != @last_error
            @logger.error("Error occurred: #{e}")
            @last_error = e
          end

          env["vagrant.error"] = e

View on GitHub (pinned to 35f3160f4a)

Solutions

  1. Treat it as expected behavior: the action was aborted and cleaned up; simply re-run the vagrant command when ready
  2. If interrupts are unwanted, avoid sending SIGINT to the process (stop CI timeouts or wrapper scripts that kill the process)
  3. If it appears without any Ctrl-C, check for parent processes or supervisors (systemd, docker stop, watchmedos) sending signals to vagrant
Defensive patterns

Strategy: try-catch

Try / catch

begin
  env.machine_action(:up, ...) # or CLI invocation
rescue Vagrant::Errors::VagrantInterrupt
  # expected on SIGINT: cleanup already ran via warden recover
  exit 130 # conventional SIGINT exit code
end

Prevention

When it happens

Trigger: User presses Ctrl-C while a middleware chain (vagrant up, halt, destroy, provision) is between actions; Runner traps SIGINT, sets environment[:interrupted] = true (lib/vagrant/action/runner.rb:96), and the next time warden.rb:35 is reached the interrupt error is raised before @actions.shift runs.

Common situations: Long-running 'vagrant up' aborted with Ctrl-C; CI jobs killed by timeout signals; scripts piping SIGINT to a vagrant process; a second interrupt arriving right as an action boundary is crossed.

Related errors


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