{"record":{"id":"02ccf5e7d1098322","repo":"hashicorp/vagrant","slug":"the-download-was-interrupted-by-an-external-signal","errorCode":null,"errorMessage":"The download was interrupted by an external signal. It did not\ncomplete.","messagePattern":"The download was interrupted by an external signal\\. It did not\ncomplete\\.","errorType":"exception","errorClass":"Vagrant::Errors::DownloaderInterrupted","httpStatus":null,"severity":"error","filePath":"lib/vagrant/util/downloader.rb","lineNumber":210,"sourceCode":"      def execute_curl(options, subprocess_options, &data_proc)\n        options = options.dup\n        options.unshift(\"-q\")\n        options << subprocess_options\n\n        # Create the callback that is called if we are interrupted\n        interrupted  = false\n        int_callback = Proc.new do\n          @logger.info(\"Downloader interrupted!\")\n          interrupted = true\n        end\n\n        # Execute!\n        result = Busy.busy(int_callback) do\n          Subprocess.execute(\"curl\", *options, &data_proc)\n        end\n\n        # If the download was interrupted, then raise a specific error\n        raise Errors::DownloaderInterrupted if interrupted\n\n        # If it didn't exit successfully, we need to parse the data and\n        # show an error message.\n        if result.exit_code != 0\n          @logger.warn(\"Downloader exit code: #{result.exit_code}\")\n          check = result.stderr.match(/\\n*curl:\\s+\\((?<code>\\d+)\\)\\s*(?<error>.*)$/)\n          if check && check[:code] == \"416\"\n            # All good actually. 416 means there is no more bytes to download\n            @logger.warn(\"Downloader got a 416, but is likely fine. Continuing on...\")\n          else\n            if !check\n              err_msg = result.stderr\n            else\n              err_msg = check[:error]\n            end\n\n            raise Errors::DownloaderError,\n              code: result.exit_code,","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/hashicorp/vagrant/blob/35f3160f4ad6edc3a9f3aa9570adfc1a4d73aaa5/lib/vagrant/util/downloader.rb#L192-L228","documentation":"Raised by Vagrant::Util::Downloader after a curl download when the Busy.busy interrupt callback fired (an external signal reached the process and set interrupted = true) — the download was cancelled by a signal, not by a curl failure. It is deliberately distinct from DownloaderError, which covers non-zero curl exits with a parsed error message.","triggerScenarios":"Ctrl-C during `vagrant box add` or any box/config download; sending SIGINT to the Vagrant process; process supervisors or wrappers (dumb-init, CI cancellation) forwarding INT to the curl child mid-transfer.","commonSituations":"Users cancelling large box downloads; CI job cancellation races; init systems with aggressive signal forwarding.","solutions":["If you cancelled deliberately, re-run the command — Vagrant restarts the download","If the INT was unintended, find what sends SIGINT (signal-forwarding wrappers, timeout tools) and reconfigure or exclude the vagrant process","For long downloads, use a stable link or mirror to shorten the interruption window"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"tries = 0\nbegin\n  Vagrant::Util::Downloader.new(url, dest).download!\nrescue Vagrant::Errors::DownloaderInterrupted\n  tries += 1\n  sleep 2\n  retry if tries < 3\nend","preventionTips":["Do not send SIGINT to vagrant during box downloads; cancel before the transfer starts","Keep box URLs local/mirrored in CI to shorten the download window","Treat interrupted downloads as retryable, not fatal"],"tags":["download","interrupt","sigint","curl","box"],"backgroundTag":"download-interrupted","analyzedSha":"35f3160f4ad6edc3a9f3aa9570adfc1a4d73aaa5","analyzedAt":"2026-08-21T13:34:32.514Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}