jdx/mise · error

remote mktemp returned an unsafe staging path: {path:?}

Error message

remote mktemp returned an unsafe staging path: {path:?}

What it means

mise creates a staging directory on the remote with mktemp (/tmp/mise-bootstrap.XXXXXX) and requires the printed path to match exactly that prefix plus a non-empty suffix with no whitespace. This error means the path mise read back from the remote's stdout does not satisfy that contract, so it refuses to rm -rf or write into it.

Source

Thrown at src/system/remote.rs:1640

    if value.starts_with('-') || value.chars().any(char::is_whitespace) {
        bail!("invalid remote {kind}: {value}");
    }
    Ok(())
}

fn validate_value(kind: &str, value: &str) -> Result<()> {
    if value.is_empty() || value.contains('\0') {
        bail!("remote {kind} cannot be empty or contain NUL");
    }
    Ok(())
}

fn validate_staging_path(path: &str) -> Result<()> {
    if !path.starts_with("/tmp/mise-bootstrap.")
        || path["/tmp/mise-bootstrap.".len()..].is_empty()
        || path.chars().any(char::is_whitespace)
    {
        bail!("remote mktemp returned an unsafe staging path: {path:?}");
    }
    Ok(())
}

fn validated_remote_command(command: &str) -> Result<String> {
    if !command.starts_with('/') || command.contains(['\0', '\n', '\r']) {
        bail!("bootstrap_command returned an unsafe mise path: {command:?}");
    }
    Ok(command.to_string())
}

fn validated_remote_command_output(output: &str) -> Result<String> {
    validated_remote_command(output.strip_suffix('\n').unwrap_or(output))
}

fn validated_absolute_remote_path_output(output: &str, kind: &str) -> Result<String> {
    let path = output.strip_suffix('\n').unwrap_or(output);
    if !path.starts_with('/') || path.contains(['\0', '\n', '\r']) {

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Reproduce: ssh <dest> 'mktemp -d /tmp/mise-bootstrap.XXXXXX' and check the output is exactly one /tmp/mise-bootstrap.<suffix> line
  2. Silence remote startup noise: remove or guard echo/printf statements in the remote user's .zshenv/.profile/.bashrc so non-interactive shells print nothing
  3. Ensure TMPDIR is unset or points at /tmp on the remote, since the check requires the literal /tmp/mise-bootstrap. prefix
  4. After fixing, re-run mise bootstrap remote; the staging path check runs on every attempt

Example fix

# before (remote ~/.zshenv): echo "welcome to buildhost" # every sh -lc call on zsh remotes emits this, polluting mktemp output
# after:
[[ -o interactive ]] && echo "welcome to buildhost"
Defensive patterns

Strategy: validation

Validate before calling

out=$(ssh "$dest" 'mktemp -d /tmp/mise-bootstrap.XXXXXX')
case "$out" in /tmp/mise-bootstrap.?*) [ "$(printf '%s' "$out" | wc -l) -eq 1 ] || echo "staging output polluted: $out" >&2;; *) echo "unsafe staging path: $out" >&2;; esac

Prevention

When it happens

Trigger: In run() after 'sh -c <staging_creation_script>': remote shell startup files (.zshenv, profile, MOTD logic) print extra lines so the trimmed stdout becomes multi-line; remote TMPDIR redirects mktemp elsewhere (path no longer starts with /tmp/mise-bootstrap.); mktemp prints a path containing a space or the template suffix is empty.

Common situations: Remote accounts with chatty dotfiles that echo text on every shell invocation (common on shared build hosts), zsh remotes where .zshenv always runs even for non-interactive commands, hardened remotes where TMPDIR points at a custom location, and CR/LF translation mangling output.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/c04701c9f7c3c1b2. Report an issue: GitHub.