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

  1. Run fsck_apfs to validate inode records
  2. Confirm the offset constants (inodePrivateIDOff, value area math) match the image's on-disk format
  3. Re-read the tree from the current volume superblock to drop any stale cached block
  4. 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

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


AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01). Data as JSON: /api/errors/a9489605451bd0a7. Report an issue: GitHub.