jdx/mise · error · eyre::Report

cannot relocate {}: not enough padding to grow load commands

Error message

cannot relocate {}: not enough padding to grow load commands ({} > {} bytes)

What it means

Mach-O relocation grows path strings (dylib install names, rpaths) by consuming the zero padding between the end of the load-command table and the first section's file data, keeping every section offset unchanged. If the rebuilt command table needs more bytes than that gap holds, the tool refuses to relocate rather than shift section data (which would invalidate offsets and code signatures).

Source

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

                    let new_cmdsize = (str_off + new.len() + 1).div_ceil(8) * 8;
                    let mut rebuilt = bytes[..str_off].to_vec();
                    rebuilt.extend_from_slice(&new);
                    rebuilt.resize(new_cmdsize.max(cmdsize), 0);
                    let len = rebuilt.len() as u32;
                    rebuilt[4..8].copy_from_slice(&len.to_le_bytes());
                    bytes = rebuilt;
                    changed = true;
                }
            }
        }
        commands.push(bytes);
    }
    if !changed {
        return Ok(false);
    }
    let new_sizeofcmds: usize = commands.iter().map(|c| c.len()).sum();
    if HEADER_SIZE_64 + new_sizeofcmds > first_data {
        bail!(
            "cannot relocate {}: not enough padding to grow load commands ({} > {} bytes)",
            path.display(),
            HEADER_SIZE_64 + new_sizeofcmds,
            first_data,
        );
    }
    let mut out = Vec::with_capacity(new_sizeofcmds);
    for c in &commands {
        out.extend_from_slice(c);
    }
    // zero everything from the header to the first data byte, then lay the
    // table down — leaves old table bytes cleanly erased
    slice[HEADER_SIZE_64..first_data].fill(0);
    slice[HEADER_SIZE_64..HEADER_SIZE_64 + out.len()].copy_from_slice(&out);
    slice[20..24].copy_from_slice(&(new_sizeofcmds as u32).to_le_bytes());
    Ok(true)
}

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Use a brew prefix at or under the length of /opt/homebrew so replacement strings fit the existing padding
  2. Clear and re-pour after changing the prefix — the pour computes replacements per prefix
  3. Report the binary upstream — such files may need a re-bottle with more header padding

Example fix

# before: long custom prefix overflows Mach-O header padding
mise settings brew.prefix /Users/me/very/long/path/homebrew  # relocation bails

# after: standard short prefix fits
mise settings brew.prefix /opt/homebrew
Defensive patterns

Strategy: try-catch

Validate before calling

// Estimate growth headroom: compute rebuilt command sizes (replacements
// applied to path strings) and compare against first_data before patching.
fn growth_fits(slice: &[u8], replacements: &[Replacement]) -> bool {
    let first_data = first_section_data_offset(slice); // min positive section offset
    let grown: usize = command_table_size_after_replace(slice, replacements);
    32 + grown <= first_data
}

Try / catch

match patch_macho(&mut content, &replacements, &path) {
    Err(e) if e.to_string().contains("not enough padding") => {
        // actionable fix: a shorter brew prefix avoids growth entirely
        return Err(e.wrap_err("install to a brew prefix no longer than /opt/homebrew, or re-bottle with more header padding"));
    }
    other => other?,
}

Prevention

When it happens

Trigger: patch_slice: after rebuilding commands with longer replacement strings, HEADER_SIZE_64 + new_sizeofcmds > first_data (first byte of section data found from LC_SEGMENT_64 section offsets). Triggered when replacement values (real brew prefix, longer than '@@HOMEBREW_PREFIX@@'-style placeholders) make dylib/rpath strings exceed the binary's available header padding.

Common situations: Installing under a brew prefix significantly longer than the bottle's build prefix; bottles packed with minimal padding (linkers like ld64 sometimes leave only a few bytes); large dylib trees with long install names.

Related errors


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