jdx/mise · error · eyre::Report

malformed load command table in {}

Error message

malformed load command table in {}

What it means

While scanning the ncmds load commands of a 64-bit Mach-O, the running offset reached the end of the table (lc_end = 32 + sizeofcmds) before all commands were read — at least 8 bytes are needed per command header. The declared table size and command count disagree, so the file is treated as malformed.

Source

Thrown at src/system/packages/brew/macho.rs:85

        // not a 64-bit LE Mach-O (32-bit or big-endian) — nothing modern on
        // arm64 macOS; leave it to the caller's generic byte-level pass
        return Ok(false);
    }
    let ncmds = u32_at(slice, 16) as usize;
    let sizeofcmds = u32_at(slice, 20) as usize;
    if HEADER_SIZE_64 + sizeofcmds > slice.len() {
        bail!("malformed Mach-O in {}", path.display());
    }

    // upper bound for growing the load-command table: the first byte of
    // section data (everything between sizeofcmds and there is padding)
    let lc_end = HEADER_SIZE_64 + sizeofcmds;
    let mut first_data = slice.len();
    {
        let mut off = HEADER_SIZE_64;
        for _ in 0..ncmds {
            if off + 8 > lc_end {
                bail!("malformed load command table in {}", path.display());
            }
            let cmd = u32_at(slice, off);
            let cmdsize = u32_at(slice, off + 4) as usize;
            if cmdsize < 8 || off + cmdsize > lc_end {
                bail!("malformed load command in {}", path.display());
            }
            if cmd == LC_SEGMENT_64 {
                let nsects = u32_at(slice, off + 64) as usize;
                for i in 0..nsects {
                    // struct section_64 is 80 bytes; offset field at +48
                    let sect = off + 72 + i * 80;
                    if sect + 80 > off + cmdsize {
                        break;
                    }
                    let file_off = u32_at(slice, sect + 48) as usize;
                    if file_off > 0 {
                        first_data = first_data.min(file_off);
                    }

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Re-download the bottle (clear cache) and retry the pour
  2. Cross-check with otool -l — inconsistency visible there confirms file corruption
  3. Report upstream if the file is reproducibly valid elsewhere
Defensive patterns

Strategy: validation

Validate before calling

// Verify ncmds commands fit within sizeofcmds before patching.
fn load_command_table_ok(slice: &[u8]) -> bool {
    let ncmds = u32_at(slice, 16) as usize;
    let lc_end = 32 + u32_at(slice, 20) as usize;
    let mut off = 32;
    for _ in 0..ncmds {
        if off + 8 > lc_end { return false; }
        off += u32_at(slice, off + 4) as usize;
    }
    true
}

Type guard

fn macho_table_walkable(slice: &[u8]) -> bool {
    macho_header_ok(slice) && load_command_table_ok(slice)
}

Try / catch

if let Err(e) = patch_macho(&mut content, &replacements, &path) {
    // log and skip this artifact rather than failing the whole pour
    warn!("{}: skipping Mach-O relocation: {e:#}", path.display());
}

Prevention

When it happens

Trigger: patch_slice first scan loop: off + 8 > lc_end before ncmds iterations complete. Produced by corrupted headers where sizeofcmds is too small for ncmds, or by fuzzed/hand-crafted Mach-O files.

Common situations: Corrupted bottle artifacts; files damaged in transit or by disk issues; test/fuzz binaries with deliberately inconsistent headers.

Understand the failure class

Related errors


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