jdx/mise · error
cannot relocate : not enough padding to grow load commands…
Error message
cannot relocate {}: not enough padding to grow load commands ({} > {} bytes) What it means
Thrown when the rewritten load-command table grows larger than the padding available before the first section data byte. Mach-O patching can only grow commands into padding between sizeofcmds and the first section — there is no room to shift section data, so relocation is refused rather than corrupting the binary.
Solutions
- Choose a target prefix that is the same length or shorter than the original path.
- Pad the original install names at build time (e.g. via `-headerpad_max_install_names` ld flag) so future rewrites fit.
- Rebuild the formula/binary from source with the desired paths instead of binary-patching.
- Fall back to symlinking the original path structure so no rewrite is needed.
Example fix
// before: patch to a longer prefix, fails
patch(&path, &[("/opt/homebrew/opt", "/very/long/custom/prefix/opt")])?;
// after: keep length compatible or rebuild
let new_prefix = "/short/pfx";
assert!(new_prefix.len() <= "/opt/homebrew".len());
patch(&path, &[("/opt/homebrew", new_prefix)])?; Defensive patterns
Strategy: validation
Validate before calling
fn rewrite_fits(old_prefix: &str, new_prefix: &str) -> bool {
new_prefix.len() <= old_prefix.len()
} Try / catch
match patch(&path, &replacements) {
Err(e) if e.to_string().contains("not enough padding") => {
eprintln!("prefix too long for binary patching; rebuilding from source");
// fall back to `brew install --build-from-source`
}
other => other?,
} Prevention
- Build with `-headerpad_max_install_names` so binaries have slack for rewrites.
- Keep relocation prefixes length-equal or shorter than the original.
- Prefer source builds over binary patching for long custom prefixes.
- Test relocation on a copy of the binary before applying to the install tree.
When it happens
Trigger: After rebuilding commands with expanded path strings (e.g. longer install names/prefixes), HEADER_SIZE_64 + new_sizeofcmds > first_data triggers the bail. Called by patch when path replacements lengthen LC_LOAD_DYLIB/LC_ID_DYLIB/LC_RPATH strings beyond available padding.
Common situations: Relocating brew bottles to a prefix longer than the original; replacing short paths with much longer custom paths; binaries with little or no inter-table padding; many dylib entries each growing slightly.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- cannot relocate : replacement for does not fit ( > bytes)
- another process is installing the mise notification helper
- app target is outside an allowed Applications directory
- [bootstrap.macos.dock].apps: expected a path relative to…
- [bootstrap.macos.dock].apps: expected an absolute or ~/…
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/324d9246646eed28.
Report an issue: GitHub.
Appendix: 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 533346cc37)