jdx/mise · error

is disabled in safe mode (MISE_SAFE=1) See…

Error message

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

What it means

Safe mode (`MISE_SAFE=1`) hard-blocks any operation that would execute code controlled by project configuration (hooks, postinstall scripts, task run commands, templates). `Settings::ensure_not_safe` is called before such operations and fails loudly rather than silently executing untrusted code. This is an intentional security boundary, not a bug.

Solutions

  1. If you trust the repository, unset the variable: `unset MISE_SAFE` (or `MISE_SAFE=0`) and re-run.
  2. Use `mise trust` after reviewing the config, then run without safe mode.
  3. In CI/agent environments, pre-install tools from trusted sources before enabling MISE_SAFE=1.

Example fix

# before
MISE_SAFE=1 mise install   # Error: install is disabled in safe mode

# after
mise trust && MISE_SAFE=0 mise install
Defensive patterns

Strategy: validation

Validate before calling

# shell: check safe mode before invoking mise commands that run code
if [ "$MISE_SAFE" = "1" ]; then echo "safe mode blocks this operation"; fi

Try / catch

# shell: explicitly opt out only for trusted repos
if [ "$MISE_SAFE" = "1" ]; then
  mise trust && MISE_SAFE=0 mise install || exit 1
fi

Prevention

When it happens

Trigger: Running mise with MISE_SAFE=1 (used by agents/CI when checking out untrusted repos) and triggering an operation that calls `Settings::ensure_not_safe`, e.g. install with postinstall hooks, task execution, or env template evaluation that runs code.

Common situations: CI jobs or AI agents that install tools from an untrusted checkout with safe mode set; running `mise install` on a fresh clone while MISE_SAFE=1 is exported in the shell profile.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at src/config/settings.rs:1905

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