jdx/mise · error · eyre::Report

local mise is not an ELF executable

Error message

local mise is not an ELF executable

What it means

Thrown by elf_interpreter when the bytes of the binary being validated for a Linux remote do not start with the \x7fELF magic. validate_default_binary_compatibility only runs for Linux-to-Linux uploads, so a non-ELF payload means the configured mise_bin is not a Linux executable — e.g. a shell wrapper script, a macOS Mach-O copied over, or a truncated/corrupted file.

Source

Thrown at src/system/remote.rs:1358

        }
        let component = std::str::from_utf8(&bytes[start..offset])
            .ok()?
            .parse()
            .ok()?;
        components.push(component);
        if bytes.get(offset) != Some(&b'.') {
            break;
        }
        offset += 1;
    }
    (!components.is_empty()).then_some(AbiVersion(components))
}

fn elf_interpreter(bytes: &[u8]) -> Result<Option<String>> {
    const ELF_MAGIC: &[u8] = b"\x7fELF";
    const PT_INTERP: u64 = 3;
    if !bytes.starts_with(ELF_MAGIC) {
        bail!("local mise is not an ELF executable");
    }
    let class = *bytes.get(4).ok_or_else(|| eyre!("truncated ELF header"))?;
    let little_endian = match bytes.get(5) {
        Some(1) => true,
        Some(2) => false,
        _ => bail!("unsupported ELF byte order"),
    };
    let (program_offset, entry_size, entry_count, offset_field, size_field) = match class {
        1 => (
            read_elf_int(bytes, 28, 4, little_endian)?,
            read_elf_int(bytes, 42, 2, little_endian)?,
            read_elf_int(bytes, 44, 2, little_endian)?,
            4,
            16,
        ),
        2 => (
            read_elf_int(bytes, 32, 8, little_endian)?,
            read_elf_int(bytes, 54, 2, little_endian)?,

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Point mise_bin at a real Linux ELF executable (file mise should say ELF 64-bit ... for Linux)
  2. Re-download or rebuild the binary if it is truncated (compare size/checksum)
  3. Remove script wrappers from the upload path; put env setup in mise_env or the remote config instead

Example fix

# before (mise.toml)
[bootstrap.remote.hosts.srv]
host = "deploy@srv"
mise_bin = "./mise-wrapper.sh"

# after
[bootstrap.remote.hosts.srv]
host = "deploy@srv"
mise_bin = "./mise"  # file ./mise -> ELF 64-bit LSB executable, x86-64
Defensive patterns

Strategy: validation

Validate before calling

// Validate the binary before pointing mise_bin at it (Linux remotes)
fn is_linux_elf(path: &Path) -> bool {
    let mut magic = [0u8; 6];
    let Ok(mut f) = std::fs::File::open(path) else { return false };
    std::io::Read::read_exact(&mut f, &mut magic).is_ok()
        && &magic[..4] == b"\x7fELF"
}

Type guard

fn is_linux_elf(path: &Path) -> bool {
    let mut magic = [0u8; 6];
    let Ok(mut f) = std::fs::File::open(path) else { return false };
    std::io::Read::read_exact(&mut f, &mut magic).is_ok()
        && &magic[..4] == b"\x7fELF"
        && matches!(magic[4], 1 | 2)
        && matches!(magic[5], 1 | 2)
}

Try / catch

match elf_interpreter(&bytes) {
    Err(e) if e.to_string().contains("not an ELF executable") => {
        // mise_bin points at a script or wrong-platform binary: fix config, don't retry
        eyre::bail!("mise_bin must be a Linux ELF executable; got {}", mise_bin.display());
    }
    other => other?,
}

Prevention

When it happens

Trigger: Setting mise_bin to a wrapper script or a wrong-platform binary (macOS mise) while the remote is Linux; a partially downloaded/corrupted local binary; a symlink target that is a script.

Common situations: Teams wrapping mise in a launcher script for env tweaks; copying binaries between platforms manually; disk/cache corruption or interrupted downloads.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/b8c5b7be163b037c. Report an issue: GitHub.