lima-vm/lima · warning
inode %d has ownership %d:%d, expected %d:%d from noowners m
Error message
inode %d has ownership %d:%d, expected %d:%d from noowners mount
What it means
chownInode only rewrites ownership when the inode currently holds the noowners placeholder uid/gid (as set on a noowners mount); if the stored ownership differs from that placeholder, it aborts. This is a safety guard: the tool only takes ownership of files that the noowners mount left with the placeholder, never files with real ownership.
Source
Thrown at pkg/apfs/chown.go:689
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.
childIdx := uint32(0)
for i := range nkeys {
kOff, _ := c.readTocEntry(blk, tocStart, i, isFixedKV)
keyStart := keyAreaStart + kOff
keyHeader := le.Uint64(blk[keyStart:])
View on GitHub (pinned to dd909d0973)
Solutions
- Remount/mount the volume with the noowners option so inodes get the placeholder ownership, then retry
- If the current ownership is already correct, skip the repair — no action needed
- Verify you are targeting the right inode/volume; a legitimate owner may already be set
- Only bypass this guard if you fully control the image and accept rewriting arbitrary ownership
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check: only run the repair when the volume was mounted with noowners
if !volumeMountedNoowners(volumeID) {
return fmt.Errorf("volume %s not mounted noowners; ownership will not be placeholder", volumeID)
} Type guard
func isOwnershipNotPlaceholder(err error) bool {
return err != nil && strings.Contains(err.Error(), "expected") && strings.Contains(err.Error(), "noowners mount")
} Try / catch
if err := c.chownInode(root, omap, maxXID, ino, uid, gid); isOwnershipNotPlaceholder(err) {
// already owned: treat as no-op success or remount with noowners and retry
return nil
} Prevention
- Mount volumes with the noowners option before running the repair
- Make the repair idempotent: skip inodes that already have final ownership
- Run the repair exactly once per mount cycle
When it happens
Trigger: Calling the ownership-repair path on a volume where the target inode already has real uid/gid values — the volume was mounted without the noowners flag, ownership was already repaired once, or the file was created with real ownership rather than the placeholder.
Common situations: Running the repair twice; mounting the volume normally (owners enforced) instead of with noowners; files whose ownership was set by the guest OS after mount.
Related errors
- inode %d value exceeds block boundary
- inode %d has mismatched private_id %d
- inode %d not found in filesystem B-tree
- chown inode %d (%#q): %w
- resolving filesystem root tree OID %d: %w
AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01).
Data as JSON: /api/errors/6a508770b2c83ae2.
Report an issue: GitHub.