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

  1. Remount/mount the volume with the noowners option so inodes get the placeholder ownership, then retry
  2. If the current ownership is already correct, skip the repair — no action needed
  3. Verify you are targeting the right inode/volume; a legitimate owner may already be set
  4. 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

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


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