{"record":{"id":"324d9246646eed28","repo":"jdx/mise","slug":"cannot-relocate-not-enough-padding-to-grow-loa","errorCode":null,"errorMessage":"cannot relocate {}: not enough padding to grow load commands ({} > {} bytes)","messagePattern":"cannot relocate (.+?): not enough padding to grow load commands \\((.+?) > (.+?) bytes\\)","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/macho.rs","lineNumber":152,"sourceCode":"                    let new_cmdsize = (str_off + new.len() + 1).div_ceil(8) * 8;\n                    let mut rebuilt = bytes[..str_off].to_vec();\n                    rebuilt.extend_from_slice(&new);\n                    rebuilt.resize(new_cmdsize.max(cmdsize), 0);\n                    let len = rebuilt.len() as u32;\n                    rebuilt[4..8].copy_from_slice(&len.to_le_bytes());\n                    bytes = rebuilt;\n                    changed = true;\n                }\n            }\n        }\n        commands.push(bytes);\n    }\n    if !changed {\n        return Ok(false);\n    }\n    let new_sizeofcmds: usize = commands.iter().map(|c| c.len()).sum();\n    if HEADER_SIZE_64 + new_sizeofcmds > first_data {\n        bail!(\n            \"cannot relocate {}: not enough padding to grow load commands ({} > {} bytes)\",\n            path.display(),\n            HEADER_SIZE_64 + new_sizeofcmds,\n            first_data,\n        );\n    }\n    let mut out = Vec::with_capacity(new_sizeofcmds);\n    for c in &commands {\n        out.extend_from_slice(c);\n    }\n    // zero everything from the header to the first data byte, then lay the\n    // table down — leaves old table bytes cleanly erased\n    slice[HEADER_SIZE_64..first_data].fill(0);\n    slice[HEADER_SIZE_64..HEADER_SIZE_64 + out.len()].copy_from_slice(&out);\n    slice[20..24].copy_from_slice(&(new_sizeofcmds as u32).to_le_bytes());\n    Ok(true)\n}\n","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/macho.rs#L134-L170","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before: long custom prefix overflows Mach-O header padding\nmise settings brew.prefix /Users/me/very/long/path/homebrew  # relocation bails\n\n# after: standard short prefix fits\nmise settings brew.prefix /opt/homebrew","handlingStrategy":"try-catch","validationCode":"// Estimate growth headroom: compute rebuilt command sizes (replacements\n// applied to path strings) and compare against first_data before patching.\nfn growth_fits(slice: &[u8], replacements: &[Replacement]) -> bool {\n    let first_data = first_section_data_offset(slice); // min positive section offset\n    let grown: usize = command_table_size_after_replace(slice, replacements);\n    32 + grown <= first_data\n}","typeGuard":null,"tryCatchPattern":"match patch_macho(&mut content, &replacements, &path) {\n    Err(e) if e.to_string().contains(\"not enough padding\") => {\n        // actionable fix: a shorter brew prefix avoids growth entirely\n        return Err(e.wrap_err(\"install to a brew prefix no longer than /opt/homebrew, or re-bottle with more header padding\"));\n    }\n    other => other?,\n}","preventionTips":["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"],"tags":["brew","macho","macos","relocation","binary-patching"],"backgroundTag":"binary-relocation-failed","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}