jdx/mise · error · eyre::Report

{operation} is disabled in safe mode (MISE_SAFE=1) See https

Error message

{operation} is disabled in safe mode (MISE_SAFE=1)
See https://mise.jdx.dev/configuration/settings.html#safe

What it means

`Settings::ensure_not_safe` (src/config/settings.rs:1426) enforces safe mode (`MISE_SAFE=1`): a security boundary that fails loudly before any operation which would execute code controlled by project configuration (remote/git task files, config-driven commands). Blocked operations must never silently fall back to something else that executes, hence a hard error with a docs link.

Source

Thrown at src/config/settings.rs:1439

            return Settings::get().safe;
        }
        // Settings not loaded (e.g. the config parse pass after Config::reset).
        // Use the value cached from the last full load, which captures `safe`
        // set via global config; before any load, fall back to the env var.
        match LAST_SAFE.load(Ordering::Relaxed) {
            0 => false,
            1 => true,
            _ => crate::env::var_is_true("MISE_SAFE"),
        }
    }

    /// Errors when safe mode (`MISE_SAFE=1`) is enabled. Call this before any
    /// operation that would execute code controlled by project configuration.
    /// Safe mode is a security boundary: blocked operations must fail loudly,
    /// never silently fall back to something that executes.
    pub fn ensure_not_safe(operation: &str) -> Result<()> {
        if Settings::safe_mode() {
            bail!(
                "{operation} is disabled in safe mode (MISE_SAFE=1)\nSee https://mise.jdx.dev/configuration/settings.html#safe"
            );
        }
        Ok(())
    }
}

fn redacted_settings_for_debug(settings: &Settings) -> Settings {
    let mut debug_settings = settings.clone();
    if debug_settings.task.cache.remote_token.is_some() {
        debug_settings.task.cache.remote_token = Some("[redacted]".to_string());
    }
    debug_settings
}

fn remove_empty_nested_settings(table: &mut toml::Table, prefix: &str) {
    table.retain(|key, value| {
        let path = if prefix.is_empty() {

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Re-run with safe mode off for this command: `MISE_SAFE=0 mise <cmd>` (or unset MISE_SAFE)
  2. Find and remove the stray `MISE_SAFE=1` export in shell profiles, CI env, or wrapper scripts
  3. If safe mode is intentional, do the blocked step manually outside mise (e.g. clone the repo and reference local task files)
  4. Read the linked settings docs section to know exactly which operations are blocked before scripting around them

Example fix

# before
$ export MISE_SAFE=1 && mise run build   # blocked
# after
$ unset MISE_SAFE && mise run build
# or scoped:
$ MISE_SAFE=0 mise run build
Defensive patterns

Strategy: validation

Validate before calling

# guard scripts that need project-config execution
if [ "${MISE_SAFE:-0}" = "1" ]; then
  echo "MISE_SAFE=1 blocks this mise operation; unset it or MISE_SAFE=0" >&2; exit 1
fi
mise run build

Try / catch

In wrappers, detect the `disabled in safe mode` message and branch: either re-exec with MISE_SAFE=0 after explicit approval, or report that the step must run outside safe mode — never silently skip it.

Prevention

When it happens

Trigger: Running a guarded operation — e.g. resolving/loading `git::` remote task files via `resolve_git_url_to_path` — while `MISE_SAFE` is `1` (or any value `env::var_is_true` accepts, since only `0` forces off).

Common situations: Hardened CI images or corporate shells that export MISE_SAFE globally; auditing an untrusted repo with safe mode and then running ordinary commands in the same shell; wrapper scripts that set MISE_SAFE and never unset it.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/ac8dc145c6d05750. Report an issue: GitHub.