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
- Use a brew prefix at or under the length of /opt/homebrew so replacement strings fit the existing padding
- Clear and re-pour after changing the prefix — the pour computes replacements per prefix
- 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
- Prefer the standard-length brew prefix (/opt/homebrew) so replacement strings never grow beyond padding
- When supporting custom prefixes, dry-run the size math per binary before pouring
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
- malformed Mach-O in {}
- malformed load command table in {}
- malformed load command in {}
- cannot relocate {}: rpath must grow but the dynamic string t
- malformed fat header in {}
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/324d9246646eed28.
Report an issue: GitHub.