jdx/mise · error

remote mktemp returned an unsafe staging path

Error message

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

What it means

`validate_staging_path` checks the path returned by the remote `mktemp -d` call: it must start with `/tmp/mise-bootstrap.`, have a non-empty suffix, and contain no whitespace. If the remote mktemp produced anything else, mise refuses to use it, since the staging path is later embedded in remote commands and an unexpected path could break quoting or be a hostile/pre-mounted location.

Solutions

  1. Ensure the remote environment uses the default mktemp behavior — unset/normalize TMPDIR for the SSH session (e.g. `ssh host 'unset TMPDIR; ...'` or in the remote shell profile)
  2. Inspect what `ssh host mktemp -d /tmp/mise-bootstrap.XXXXXX` actually returns and align it with the expected prefix
  3. If the host cannot provide /tmp paths, run the bootstrap without the remote transport or pre-provision a compliant staging directory

Example fix

// before
# remote profile sets TMPDIR=/var/user tmp
$ ssh host mktemp -d /tmp/mise-bootstrap.XXXXXX
/var/tmp/mise-bootstrap.abc   # unexpected prefix
// after
# normalize remote env
$ ssh host 'TMPDIR=/tmp mktemp -d /tmp/mise-bootstrap.XXXXXX'
/tmp/mise-bootstrap.x9K2q
Defensive patterns

Strategy: validation

Validate before calling

# verify the remote mktemp path shape before running the flow
p=$(ssh "$host" 'mktemp -d /tmp/mise-bootstrap.XXXXXX') \\\n  || exit 1
case "$p" in
  /tmp/mise-bootstrap.?*) ;;
  *) echo "unexpected staging path: $p"; exit 1;;
esac

Type guard

fn is_safe_staging_path(p: &str) -> bool {
    p.starts_with("/tmp/mise-bootstrap.")
        && p.len() > "/tmp/mise-bootstrap.".len()
        && !p.chars().any(char::is_whitespace)
}

Try / catch

let path = remote_mktemp().await?;
if !is_safe_staging_path(&path) {
    // normalize TMPDIR on the host and retry once
    retry_with_default_tmpdir().await?;
}

Prevention

When it happens

Trigger: Remote `mktemp -d` template yields a path outside the expected `/tmp/mise-bootstrap.*` prefix (custom TMPDIR, restricted tmp layout), an empty suffix, or a path containing spaces.

Common situations: Remote host with TMPDIR set to a non-/tmp location; hardened/immutable /tmp with unusual mktemp wrappers; site-specific mktemp templates producing paths with spaces.

Related errors


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

Appendix: source

Thrown at src/system/remote.rs:2189

    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 533346cc37)