jdx/mise · warning

remote cleanup failed with

Error message

remote cleanup failed with {status}

What it means

The remote cleanup step runs `ssh host rm -rf -- <path>` wrapped in a 5-second timeout and bails if the remote `rm` exits non-success. This is a best-effort delete of the bootstrap staging directory, so failure means the temp artifact may be left behind on the remote host.

Solutions

  1. SSH in manually and remove the staging directory: `ssh host 'rm -rf /tmp/mise-bootstrap.*'`
  2. Check ownership/permissions of the staging dir and remove as the correct user or with elevated rights
  3. Increase tolerance for slow links by ensuring SSH connection reuse (ControlMaster) so subsequent commands are fast

Example fix

// before
# leftover staging dir owned by root
ssh host 'ls -ld /tmp/mise-bootstrap.*'  # root:root
// after
ssh host 'sudo rm -rf /tmp/mise-bootstrap.*'  # or fix ownership first
Defensive patterns

Strategy: try-catch

Validate before calling

# ensure cleanup will succeed: session owns the staging dir
ssh "$host" "test -O \\"$staging\\" && rm -rf -- \\"$staging\\""

Try / catch

if let Err(e) = cleanup().await {
    // non-fatal: log leftover path for manual removal
    eprintln!("warning: {e}; leftover /tmp/mise-bootstrap.* may remain on host");
}

Prevention

When it happens

Trigger: The `rm -rf` command on the remote host returns non-zero within the 5s window: permission denied on the staging dir, remote shell failure, or the command being killed at the 5s timeout... though the timeout case surfaces as 'remote cleanup timed out'; this error is specifically a non-success exit status.

Common situations: Slow/high-latency SSH connection making rm appear to fail or hang; staging path owned by another user after a partial bootstrap; remote account restricted (rbash, limited sudo).

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/9e266abc0d85b194. Report an issue: GitHub.

Appendix: source

Thrown at src/system/remote.rs:2107

                    shell_quote(remote)
                ),
            ],
            file,
        )
        .await
    }

    async fn cleanup(&self, path: &str) -> Result<()> {
        validate_staging_path(path)?;
        let mut command = tokio::process::Command::new(&self.ssh);
        command
            .args(self.args(false, &["rm", "-rf", "--", path]))
            .kill_on_drop(true);
        let status = tokio::time::timeout(std::time::Duration::from_secs(5), command.status())
            .await
            .map_err(|_| eyre!("remote cleanup timed out"))??;
        if !status.success() {
            bail!("remote cleanup failed with {status}");
        }
        Ok(())
    }

    async fn close(&self) {
        let Some(control_path) = &self.control_path else {
            return;
        };
        let mut command = tokio::process::Command::new(&self.ssh);
        command
            .kill_on_drop(true)
            .args(["-S"])
            .arg(control_path)
            .args(["-O", "exit"])
            .arg(self.host.destination())
            .stdout(Stdio::null())
            .stderr(Stdio::null());
        let status =

View on GitHub (pinned to 533346cc37)