jdx/mise · error

remote host ' ' bootstrap command contains NUL

Error message

remote host '{}' bootstrap command contains NUL

What it means

A bootstrap_command is transmitted to the remote host as a NUL-terminated/executable command string; embedding a NUL byte would silently truncate or corrupt it, so validate_with_source rejects any bootstrap command containing '\0'.

Solutions

  1. Inspect the configured command for stray NUL bytes and remove them.
  2. If the command comes from a file, re-export it as clean UTF-8 text without null bytes.
  3. Fix the generating code to strip/forbid '\0' when assembling the command string.
  4. Reference the script by path (e.g. upload then run) instead of embedding raw bytes.

Example fix

// before
bootstrap_command = some_bytes_as_string_with_nul
// after
bootstrap_command = "bash /opt/bootstrap.sh"
Defensive patterns

Strategy: validation

Validate before calling

function assertNoNul(host) {
  if (host.bootstrap_command && host.bootstrap_command.includes('\0')) {
    throw new Error(`remote host '${host.name}' bootstrap command contains NUL`);
  }
}

Prevention

When it happens

Trigger: A remote host entry sets bootstrap_command to a string containing a NUL byte (typically from binary data, bad string handling, or reading the script from a corrupted source); validate() or apply_overrides() bail.

Common situations: Programmatically building the command from byte buffers or file contents that include '\0'; template substitution injecting null bytes; copy-paste of binary content into config.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/system/remote.rs:405

        }
        for exclude in &self.exclude {
            validate_value("archive exclude", exclude)?;
        }
        for env in &self.mise_env {
            validate_value("mise environment", env)?;
            if env.contains(',') {
                bail!("remote mise environment cannot contain a comma: {env}");
            }
        }
        if let Some(remote_mise) = &self.remote_mise {
            validate_remote_executable(remote_mise)?;
        }
        if self
            .bootstrap_command
            .as_ref()
            .is_some_and(|command| command.contains('\0'))
        {
            bail!("remote host '{}' bootstrap command contains NUL", self.name);
        }
        Ok(())
    }
}

pub(crate) async fn run(
    host: &RemoteHost,
    options: &RemoteRunOptions,
    artifacts: &mut RemoteArtifactResolver,
    repository: Option<&super::remote_repository::Source>,
) -> Result<()> {
    run_inner(host, options, artifacts, repository).await
}

/// Observe cancellation inside resource ownership, so cleanup can still be awaited.
pub(crate) async fn interruptible<T>(
    operation: impl std::future::Future<Output = Result<T>>,
) -> Result<T> {

View on GitHub (pinned to 533346cc37)