jdx/mise · error · eyre::Report
malformed fat arch in {}
Error message
malformed fat arch in {} What it means
Within a fat Mach-O, each arch entry carries an offset and size delimiting its slice. An entry whose offset+size exceeds the file length describes data that does not exist, so the slicer refuses to patch rather than index out of bounds or patch the wrong bytes.
Source
Thrown at src/system/packages/brew/macho.rs:193
return Ok(false);
}
let be_magic = u32::from_be_bytes(content[..4].try_into().unwrap());
if be_magic == FAT_MAGIC_BE {
let nfat = u32::from_be_bytes(content[4..8].try_into().unwrap()) as usize;
let mut changed = false;
// collect slice ranges first (fat headers are big-endian)
let mut ranges = vec![];
for i in 0..nfat {
let entry = 8 + i * 20;
if entry + 20 > content.len() {
bail!("malformed fat header in {}", path.display());
}
let offset =
u32::from_be_bytes(content[entry + 8..entry + 12].try_into().unwrap()) as usize;
let size =
u32::from_be_bytes(content[entry + 12..entry + 16].try_into().unwrap()) as usize;
if offset + size > content.len() {
bail!("malformed fat arch in {}", path.display());
}
ranges.push(offset..offset + size);
}
for range in ranges {
changed |= patch_slice(&mut content[range], replacements, path)?;
}
Ok(changed)
} else {
patch_slice(content, replacements, path)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::system::packages::brew::relocate::tests::test_replacements;
/// build a minimal 64-bit Mach-O: header + LC_SEGMENT_64 (one section)View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Re-download the bottle from scratch and retry the pour
- Verify with lipo -detailed_info — slices outside the file will fail there too
- Report upstream if the artifact is reproducibly valid in other tooling
Defensive patterns
Strategy: validation
Validate before calling
// Every fat-arch slice must lie inside the file before patching.
fn fat_slices_in_bounds(content: &[u8]) -> bool {
if content.len() < 8 { return false; }
let nfat = u32::from_be_bytes(content[4..8].try_into().unwrap()) as usize;
(0..nfat).all(|i| {
let e = 8 + i * 20;
e + 20 <= content.len() && {
let off = u32::from_be_bytes(content[e + 8..e + 12].try_into().unwrap()) as usize;
let size = u32::from_be_bytes(content[e + 12..e + 16].try_into().unwrap()) as usize;
off.checked_add(size).is_some_and(|end| end <= content.len())
}
})
} Type guard
fn fat_macho_wellformed(content: &[u8]) -> bool {
fat_header_ok(content) && fat_slices_in_bounds(content)
} Try / catch
match patch_macho(&mut content, &replacements, &path) {
Ok(changed) => changed,
Err(e) => { warn!("{}: {e:#}", path.display()); false }
} Prevention
- Re-download bottles whose size differs from the manifest instead of patching them
- Validate universal binaries with lipo -detailed_info before automated relocation
When it happens
Trigger: patch_macho fat path: offset + size > content.len() for some arch entry. Produced by truncated files (slices at the end missing), corrupted arch tables, or files with stale headers after being truncated/re-padded.
Common situations: Partially downloaded universal bottles; artifacts damaged in transit; fat binaries post-processed by tools that stripped slices without updating the header.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- malformed fat header in {}
- malformed Mach-O in {}
- malformed load command table in {}
- malformed load command in {}
- cannot relocate {}: not enough padding to grow load commands
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/8ad53328e1d7998e.
Report an issue: GitHub.