How Mise Works
Learn how the mise version manager works on Mac, from installation to shell activation and automatic version switching. Understand how mise modifies PATH, what shell hooks do, and how to troubleshoot when mise activation fails. A technical guide to the internals of mise on macOS with zsh.
Mise is a multi-language version manager that automatically switches language or tool versions as you move between project directories. This article explains what happens under the hood when you install and activate mise. You do not need this knowledge to use mise, but understanding the internals helps when troubleshooting and helps you know what the tool does to your shell environment. See Mise on Mac for an overview of mise features and language-specific guides.
You don't need to know how mise works to set up your Mac for software development. What's more important is our complete guide to set up a Mac for software development.
Before you get started
You'll need a terminal application to install and use the mise version manager. Apple includes the Mac terminal but I prefer Warp Terminal. Warp is an easy-to-use terminal application, with AI assistance to help you learn and remember terminal commands. Download Warp Terminal now; it's FREE and worth a try.
Article scope and defaults
This guide covers mise on macOS with Apple Silicon (M-series) running the default zsh shell, using the recommended PATH-based shell hooks activation (not the shims fallback). Mise also supports Intel Macs and other shells such as bash and fish, but those variations are outside the scope of our Mac Install Guide coverage. See the official mise documentation for other configurations.
For installation steps, see Install Mise on Mac. For configuration details, see Mise Configuration on Mac. For troubleshooting, see command not found: mise.
Two steps to a working mise setup
Getting mise fully working requires two separate steps: installation and activation.
The first step is installing the mise binary program. This makes the mise command available in your terminal. The second step is activating mise in your shell. Skipping the second step, activation, is the most common source of trouble for new users. Activation is what enables automatic tool switching when you move between project directories.
Installation alone gives you the mise command. Activation is what makes mise powerful when you switch projects.
Install the binary program
If you install mise with Homebrew on Apple Silicon (M-series), the binary executable is installed in /opt/homebrew/bin/mise. That directory is already on your Mac PATH (you set it up when you first installed Homebrew), so mise works immediately after installation.
$ brew install mise
$ which mise
/opt/homebrew/bin/mise
$ mise --version
However, mise is not yet "activated" (that is a Mise term). Homebrew does not modify your shell configuration files. You can run mise commands, but tools will not automatically switch versions when you navigate into different project directories.
I recommend using Homebrew to install mise, as Homebrew keeps all your command line tools "under one roof." If you prefer not to use Homebrew, mise offers two curl install scripts. Understanding the difference matters for troubleshooting.
Two curl install scripts
Mise provides two different curl install URLs, as an alternative to Homebrew installation, documented on different pages of the mise website, which can cause confusion.
The basic script installs the binary only:
$ curl https://mise.run | sh
This places the binary at ~/.local/bin/mise and does nothing else. It does not modify your shell configuration. You must set up activation yourself (see below).
The zsh-specific script installs the binary and sets up activation:
$ curl https://mise.run/zsh | sh
This does two things. First, it runs the same basic install script to place the binary at ~/.local/bin/mise. Second, it appends the activation line to your ~/.zshrc:
eval "$($HOME/.local/bin/mise activate zsh)" # added by https://mise.run/zsh
If you used the zsh-specific script, activation is already configured. You just need to restart your terminal (or run source ~/.zshrc) for it to take effect. If you used the basic script, you need to add the activation line yourself and restart your terminal.
Activate mise in your shell
Activation enables the automatic tool-switching behavior. If you installed mise using Homebrew, you must add this line to your shell configuration file (~/.zshrc):
eval "$(mise activate zsh)"
If you installed with either curl script, the binary program lives in ~/.local/bin/mise. That directory is not on your PATH by default, so you cannot just type mise yet. This creates a chicken-and-egg situation: how do you run mise to set up activation if the shell cannot find mise?
The answer is that the activation line must use the full path to the binary. You never need to add ~/.local/bin to your PATH permanently (in ~/.zprofile or anywhere else). Instead, every time zsh starts and processes ~/.zshrc, it runs the activation line using the full path. The mise activate output script detects that the directory for the Mise binary is not on PATH and prepends it automatically. So ~/.local/bin ends up on PATH as a side effect of activation, not because you configured it separately. The full path is only needed in this one line in ~/.zshrc:
eval "$($HOME/.local/bin/mise activate zsh)"
If you used the mise.run/zsh script, this line is already in your ~/.zshrc (check to be sure). If you used the basic mise.run script, add it yourself. See Install Mise on Mac for complete activation instructions. The article .zshrc or .zprofile explains the differences between shell configuration files.
Important: do not skip the activation step. After brew install mise, the mise command works immediately, so it is easy to think you are done. After either curl script, mise is not even available by name until activation runs (unless you previously added ~/.local/bin to your PATH, perhaps when setting up other tools).
Without setting up activation, if the mise exec command is available (because its directory is on the PATH from Homebrew or a previously configured ~/.local/bin), the mise exec command will work, but mise exec is a manual, one-off way to run tools. Activation gives you the seamless, automatic version switching that makes mise worth using.
What activation does under the hood
When your shell starts up and processes ~/.zshrc, it runs mise activate zsh, which outputs a block of shell script. The eval command executes that script in your current shell session. The script does four things.
First, it sets the environment variable MISE_SHELL=zsh. Mise uses this to know which shell it is working with.
Second, it saves your original PATH as __MISE_ORIG_PATH. This snapshot of your PATH before mise touches it lets mise cleanly add and remove tool paths without corrupting your PATH over time.
Third, it defines a mise() shell function. This wrapper around the mise binary program intercepts certain commands (such as mise shell) that need to modify your current shell session's environment. Most commands pass through to the binary unchanged.
Fourth, it registers two shell hooks: a precmd hook that runs before every prompt (every time you press Enter) and a chpwd hook that runs every time you change directories.
Shell hooks
Every time a hook fires (on prompt display or directory change), it runs:
eval "$(mise hook-env -s zsh)"
This is where the core behavior lives. The hook-env command reads the mise configuration for your current directory, looking for mise.toml or .tool-versions files and walking up the directory tree. It determines which tools and versions are needed, then locates the installation paths for those tools (for example, ~/.local/share/mise/installs/node/24.0.0/bin). It rebuilds your PATH by inserting those tool paths ahead of your original PATH. It sets any environment variables defined in your project mise.toml file. Finally, it outputs shell export commands for all of the above.
The shell evaluates these commands, and now node, python, and other tools resolve to the versions mise manages. When you navigate to a different project, the hook fires again, removes the old project tool paths, and inserts the new ones. It is implemented in Rust, so it is fast.
PATH order
Mise reconstructs your PATH in a specific order:
[pre-mise paths] → [mise config paths] → [mise tool paths] → [original PATH]
This ensures mise-managed tools take priority, while preserving any PATH entries you added in your shell configuration before or after the activation line.
Compare mise activation to other tools
If you have installed tools such as rbenv that use a PATH-based approach, you may be used to adding something like export PATH="$HOME/.rbenv/bin:$PATH" to your shell configuration. That is a one-time, static PATH modification to enable a tool to be found by the shell.
Mise activation is dynamic. Instead of pointing to a single fixed directory, mise rewrites PATH on every prompt and directory change to point to the exact tool versions your current project needs. This is how it seamlessly switches between Node.js 20 in one project and Node.js 24 in another, with no manual intervention.
Shims mode
Mise also supports a shims mode as a fallback when the more efficient zsh shell hooks cannot be used. Shims mode is useful for non-interactive environments such as CI/CD pipelines and some IDEs that cannot use shell hooks.
A shim, in this context, is a small executable placed on your PATH whose name matches a real command (such as node or python). The shell finds the shim first, and the shim decides which underlying binary to run based on your project configuration. The term comes from systems programming and compatibility engineering, where a shim is a thin layer that intercepts calls between two components. Windows uses application compatibility shims to redirect API calls. Unix systems use LD_PRELOAD libraries and wrapper scripts for similar interposition.
The specific pattern of dropping a wrapper script earlier on PATH to override a real command has been around for decades in compiler toolchains and package managers. Ruby's rbenv (created in 2011) popularized a clean, user-level version of this pattern for language version management: it generates per-command shims for every Ruby and gem executable into ~/.rbenv/shims, then ensures that directory is at the front of PATH. Each shim delegates to rbenv exec to select the correct Ruby version based on project configuration. Python's pyenv copied this design directly from rbenv, and the pattern spread to other tools.
Mise uses the same shim concept as a fallback. Activate shims mode by adding --shims to the activation line:
eval "$(mise activate zsh --shims)"
This adds ~/.local/share/mise/shims to your PATH. That directory contains small wrapper scripts for each tool executable. When you run node, the shim intercepts the call, determines the correct version for your current directory, and executes it.
Shims are simpler but less powerful than full activation. Shims do not set environment variables automatically, do not run project-specific hooks, and only resolve the tool version at the moment you run a command rather than proactively setting up the environment.
Full activation (without --shims) is the recommended default for local development. Use shims if you are running Mise on a remote server or in an environment where shell hooks are not available.
Why Homebrew does not activate mise automatically for zsh
Homebrew installation of Mise activates mise automatically for the fish shell but not for the macOS default zsh shell or older bash shells. Fish has a vendor_conf.d directory where packages can drop in configuration files that get auto-sourced on shell startup. The Homebrew formula places a mise-activate.fish file there, so fish users get activation with zero configuration.
Zsh has no equivalent mechanism. Zsh only reads specific dotfiles (.zshenv, .zprofile, .zshrc) in your home directory. There is no system-wide "drop a file here and it auto-loads" directory. The only way to activate mise in zsh is to modify your own ~/.zshrc, and Homebrew formulas (rightly) do not edit user dotfiles.
Verify mise activation
After adding the activation line and restarting your terminal, verify that mise is active.
$ echo $MISE_SHELL
zsh
If this prints nothing, activation is not running. Run mise doctor for a comprehensive health check.
$ mise doctor
The output shows whether activation is working, whether shims are on PATH, and what configuration files mise found. Look for "no problems found" in the output.
Troubleshoot activation problems
command not found: mise
If you installed with Homebrew, verify the binary program exists and Homebrew's directory is on your PATH.
$ ls /opt/homebrew/bin/mise
$ echo $PATH | tr ':' '\n' | grep homebrew
You should see /opt/homebrew/bin in the output. If it is missing, check that your ~/.zprofile contains Homebrew's shell setup. See command not found: brew for a full guide.
If you installed with a curl script, verify the binary exists at ~/.local/bin/mise. If the file exists but mise is not found by name, that is expected before activation. Unlike Homebrew, the curl scripts do not place the binary in a directory already on your PATH. The mise activate output script prepends the binary's directory to PATH each time the shell starts. If you used the basic mise.run script, make sure you have added the activation line to ~/.zshrc yourself. If you used the mise.run/zsh script, the activation line should already be there. In either case, restart your terminal after adding the line.
For a complete troubleshooting walkthrough, see command not found: mise.
Tools don't work after installing
If mise works but tools such as node or python are not found, mise is installed but not activated.
Check if mise is activated:
$ echo $MISE_SHELL
If this prints nothing, activation is not running. Check your ~/.zshrc for the activation line:
$ grep "mise activate" ~/.zshrc
Common mistakes with the activation line include: missing eval (the line must be eval "$(mise activate zsh)", not just mise activate zsh), specifying the wrong shell (make sure it says zsh, not bash), or having the line commented out.
After fixing, restart your terminal and verify:
$ echo $MISE_SHELL
Also make sure you have told mise which tool to use in the current directory. Without a mise.toml file, mise does not know which tools to activate.
$ mise use node@24
Wrong version of a tool
Check what mise thinks is active and which binary program the shell is finding.
$ mise ls
$ which node
The which output should point to a path like ~/.local/share/mise/installs/node/24.x.x/bin/node. If it points to /usr/local/bin/node or /opt/homebrew/bin/node, something earlier in your PATH is taking priority.
Check your PATH for conflicts:
$ echo $PATH | tr ':' '\n'
Look for other version managers (nvm, rbenv, pyenv) that might be inserting paths before mise. Their activation lines in ~/.zshrc should be removed or placed after mise activation. See Mac PATH for a deeper explanation of how PATH works on macOS.
Tools not switching when changing directories
Verify that the shell hooks are registered:
$ echo $precmd_functions
The output should include _mise_hook_precmd. If it does not, activation did not complete. Test hook-env manually to see what it computes:
$ mise hook-env -s zsh
This prints the export commands that would normally be evaluated. Check if the output includes PATH entries for your expected tools. Also verify that each project directory has a mise.toml or .tool-versions file specifying the tools and versions needed. See Mise Configuration on Mac for details on configuration files.
Start fresh if things are badly tangled
If troubleshooting individual issues has not helped, reset your mise activation.
Remove the mise activation line from ~/.zshrc. Use a terminal text editor to edit your shell configuration file and delete the eval "$(mise activate ...)" line. Restart your terminal. Then re-add the activation line:
$ echo 'eval "$(mise activate zsh)"' >> ~/.zshrc
Restart your terminal again and run mise doctor to verify.
Get more diagnostic information
For any issue, these commands help diagnose problems:
$ mise doctor
$ MISE_DEBUG=1 mise ls
$ MISE_TRACE=1 mise hook-env -s zsh
The MISE_DEBUG and MISE_TRACE environment variables enable verbose output that shows what mise is doing at each step.
Continue setting up your Mac
Don't miss the full visual roadmap and checklist that shows how to set up a Mac for software development, with all the essential tools and settings you might not yet know about.