jdx/mise · error
failed to open the editor `{program}`: {err} Set $EDITOR or
Error message
failed to open the editor `{program}`: {err}
Set $EDITOR or $VISUAL to an editor mise can run. What it means
`mise editor` resolves an editor from $VISUAL/$EDITOR (or defaults) and spawns it with the target file. If the child process fails to launch at all, mise bails naming the attempted program and both controlling variables, because std's raw 'program not found' error alone is unhelpful.
Source
Thrown at src/cli/editor.rs:23
/// Open `file` in the user's editor and wait for it to exit.
///
/// The wait is part of the contract, not an accident of blocking IO: `mise dotfiles edit --apply`
/// converges the target as soon as this returns, so an editor that detached would apply a file the
/// user had not saved yet.
pub(super) fn open_in_editor(file: &Path) -> Result<()> {
open_in_editor_with(&crate::env::EDITOR, file)
}
/// Takes the editor rather than reading it, so the failure path can be driven from a test.
fn open_in_editor_with(editor: &str, file: &Path) -> Result<()> {
let (program, mut args) = split_editor_command(editor)?;
args.push(file.as_os_str().into());
if let Err(err) = crate::cmd::cmd(&program, args).run() {
// Name the program and the variables that choose it. A child that never starts comes back
// as std's bare "program not found" (see the note in `backend::which_spawnable`), which
// says neither what mise tried to run nor where the name came from — and the default is
// the name most likely to be missing.
bail!(
"failed to open the editor `{program}`: {err}\n\
Set $EDITOR or $VISUAL to an editor mise can run."
);
}
Ok(())
}
fn split_editor_command(editor: &str) -> Result<(String, Vec<OsString>)> {
let mut parts = shell_words::split(editor)
.map_err(|e| eyre!("failed to parse EDITOR/VISUAL value {:?}: {}", editor, e))?
.into_iter();
let program = parts
.next()
.ok_or_else(|| eyre!("EDITOR/VISUAL is empty"))?;
Ok((program, parts.map(Into::into).collect()))
}
View on GitHub (pinned to afd2eddd3a)
Solutions
- Set $EDITOR or $VISUAL to a single executable on PATH, e.g. export EDITOR=vim (or nvim, nano)
- If you need arguments, use a wrapper script or an editor that needs none, since mise execs the program directly
- Verify the program exists: which "$EDITOR"; install it or fix PATH
Example fix
// before export EDITOR="code --wait" // after export EDITOR="nvim" # or create a wrapper: ~/bin/code-wait "#!/bin/sh\nexec code --wait \"$@\"" then export EDITOR=code-wait
Defensive patterns
Strategy: try-catch
Validate before calling
# verify the editor is a single runnable executable before invoking mise
cmd=${EDITOR:-${VISUAL:-vi}}
command -v "${cmd%% *}" >/dev/null || echo "editor $cmd not found on PATH" Try / catch
if ! mise editor <file> 2>&1; then echo "failed: set EDITOR to a single binary, e.g. export EDITOR=nvim" fi
Prevention
- Set EDITOR/VISUAL to a single executable name, not a command with flags
- Confirm the editor is installed and on PATH (command -v "$EDITOR")
- Use a wrapper script when argument-passing editors (e.g. code --wait) are required
When it happens
Trigger: open_in_editor_with runs the resolved program and the spawn fails — the program name does not exist on PATH, lacks execute permission, or $EDITOR/$VISUAL contains a command with arguments/shell syntax mise cannot exec directly.
Common situations: EDITOR set to `code --wait` in an environment where mise runs it without a shell; EDITOR pointing to an uninstalled editor; VISUAL left from another machine; PATH missing in the exec context.
Related errors
- Environment variable {key} not found
- --stdin requires exactly one environment variable key
- {}@{requested} is not installed{resolved} hint: run `mise in
- No executable found for configured tool: {bin_name} The inst
- {bin_name} is a mise bin however it is not currently active.
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/08b6cf4a2dd1c4c6.
Report an issue: GitHub.