lima-vm/lima · error
inode %d has mismatched private_id %d
Error message
inode %d has mismatched private_id %d
What it means
chownInode found an inode record whose private_id field does not equal the inode number being targeted. In APFS the inode value's private_id must match its key's inode number; a mismatch means the located value is not actually this inode (or the record is corrupt), so the write is refused to avoid chowning the wrong record.
Source
Thrown at pkg/apfs/chown.go:683
if isLeaf {
for i := range nkeys {
kOff, vOff := c.readTocEntry(blk, tocStart, i, isFixedKV)
keyStart := keyAreaStart + kOff
keyHeader := le.Uint64(blk[keyStart:])
if keyHeader != targetKeyHeader {
continue
}
// Found the inode. Validate before writing.
valStart := valueAreaEnd - vOff
if valStart+inodeGroupOff+4 > c.blockSize {
return fmt.Errorf("inode %d value exceeds block boundary", inodeNum)
}
if privateID := le.Uint64(blk[valStart+inodePrivateIDOff:]); privateID != inodeNum {
return fmt.Errorf("inode %d has mismatched private_id %d", inodeNum, privateID)
}
currentUID := le.Uint32(blk[valStart+inodeOwnerOff:])
currentGID := le.Uint32(blk[valStart+inodeGroupOff:])
if currentUID != noownersPlaceholderID || currentGID != noownersPlaceholderID {
return fmt.Errorf("inode %d has ownership %d:%d, expected %d:%d from noowners mount",
inodeNum, currentUID, currentGID, noownersPlaceholderID, noownersPlaceholderID)
}
le.PutUint32(blk[valStart+inodeOwnerOff:], uid)
le.PutUint32(blk[valStart+inodeGroupOff:], gid)
updateChecksum(blk)
return c.writeBlock(blkAddr, blk)
}
return fmt.Errorf("inode %d not found in filesystem B-tree", inodeNum)
}
// Internal node: descend.View on GitHub (pinned to dd909d0973)
Solutions
- Run fsck_apfs to validate inode records
- Confirm the offset constants (inodePrivateIDOff, value area math) match the image's on-disk format
- Re-read the tree from the current volume superblock to drop any stale cached block
- Do not force the write — chowning a mismatched record would corrupt the filesystem
Defensive patterns
Strategy: type-guard
Validate before calling
// Only target inodes known to carry the noowners placeholder
if !inodeIsOwnedByNoownersMount(volumeID, inodeNum) {
return fmt.Errorf("inode %d not in noowners state; remount with noowners", inodeNum)
} Type guard
func isPrivateIDMismatch(err error) bool {
return err != nil && strings.Contains(err.Error(), "mismatched private_id")
} Try / catch
if err := c.chownInode(root, omap, maxXID, ino, uid, gid); isPrivateIDMismatch(err) {
return fmt.Errorf("refusing write: record is not this inode; fsck required: %w", err)
} Prevention
- Never bypass the private_id check; it protects against chowning the wrong record
- Re-read blocks freshly instead of reusing caches across external modifications
- Verify offset constants against the image's on-disk format version
When it happens
Trigger: The B-tree lookup matched a key header for inodeNum but the value at that offset belongs to a different object (corruption, wrong offset constants like inodePrivateIDOff, or toc/value offset desync), or the image was modified by another tool between reads.
Common situations: Images altered by third-party APFS tools; constants drifted for a newer on-disk format; corruption that still passes checksum because the wrong block was cached.
Related errors
- inode %d value exceeds block boundary
- inode %d has ownership %d:%d, expected %d:%d from noowners m
- inode %d not found in filesystem B-tree
- resolving filesystem root tree OID %d: %w
- resolving path %#q: %w
AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01).
Data as JSON: /api/errors/a9489605451bd0a7.
Report an issue: GitHub.