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
- 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)
- Inspect what `ssh host mktemp -d /tmp/mise-bootstrap.XXXXXX` actually returns and align it with the expected prefix
- 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
- Keep TMPDIR unset/default in remote shell profiles used by bootstrap
- Verify remote mktemp output shape when hardening hosts
- Avoid custom mktemp wrappers or templates on build hosts
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
- invalid remote
- remote host ' ' port must be greater than zero
- remote cannot be empty or contain NUL
- a repository preview directory requires a dry run
- bootstrap_command returned an unsafe mise path
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)