lima-vm/lima · error

chown inode %d (%#q): %w

Error message

chown inode %d (%#q): %w

What it means

Thrown by pkg/apfs.Chown when chownInode locates the inode for a resolved path but refuses or fails to patch its owner/group. chownInode validates that the inode's current uid/gid equal the "noowners" placeholder IDs written by a no-ownership mount; a mismatch, a missing inode record, checksum failure, block write failure, or boundary/private_id validation error is wrapped here with the inode number and path.

Source

Thrown at pkg/apfs/chown.go:43

	defer c.close()

	vol, err := c.findVolume(volumeRole)
	if err != nil {
		return err
	}

	fsRootPhys, err := c.omapLookup(vol.omapTreeAddr, vol.rootTreeOID, vol.latestXID)
	if err != nil {
		return fmt.Errorf("resolving filesystem root tree OID %d: %w", vol.rootTreeOID, err)
	}

	for _, path := range paths {
		inodeNum, err := c.resolvePath(fsRootPhys, vol.omapTreeAddr, vol.latestXID, path)
		if err != nil {
			return fmt.Errorf("resolving path %#q: %w", path, err)
		}
		if err := c.chownInode(fsRootPhys, vol.omapTreeAddr, vol.latestXID, inodeNum, uid, gid); err != nil {
			return fmt.Errorf("chown inode %d (%#q): %w", inodeNum, path, err)
		}
	}
	return nil
}

// container holds the open disk image file, the byte offset where
// the APFS container starts (nonzero for GPT-partitioned disks),
// and the APFS block size.
type container struct {
	f          *os.File
	baseOffset int64 // byte offset of APFS container within file
	blockSize  uint32
}

// volumeInfo holds resolved volume information.
type volumeInfo struct {
	omapTreeAddr uint64 // physical address of volume omap B-tree root
	rootTreeOID  uint64 // virtual OID of filesystem B-tree root

View on GitHub (pinned to dd909d0973)

Solutions

  1. Run Chown only once per image; verify current ownership state before re-running, or regenerate the image to restore the noowners placeholder state.
  2. Check the disk image file is writable (os.O_RDWR) and not opened by another process.
  3. Ensure the volume was created/mounted with ownership disabled (noowners) so inodes carry the placeholder IDs Chown expects.
  4. If the image is corrupted, rebuild or re-export it.

Example fix

// before
_ = apfs.Chown(disk, apfs.VolRoleData, 501, 20, paths...) // run in a retry loop; second run fails
// after
if err := apfs.Chown(disk, apfs.VolRoleData, 501, 20, paths...); err != nil {
    log.Fatal(err) // do not retry: placeholder ownership is consumed by the first successful run
}
Defensive patterns

Strategy: try-catch

Validate before calling

// run on an image in the pristine noowners state; skip if already chowned
if alreadyChowned[diskPath] {
    return nil
}

Try / catch

if err := apfs.Chown(disk, role, uid, gid, paths...); err != nil {
    if strings.Contains(err.Error(), "expected") && strings.Contains(err.Error(), "noowners") {
        // ownership placeholder already replaced: treat as success or rebuild image
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling apfs.Chown on a volume whose target inode does not currently hold the noowners placeholder ownership (e.g. the volume was previously chowned, or was mounted WITH ownership honored), the inode record is absent from the filesystem B-tree, or the underlying writeBlock fails (read-only file, full disk, I/O error).

Common situations: Running Chown twice — the second run fails because the ownership no longer matches the placeholder; the image was mounted by macOS with owners enabled between operations; the disk image file lacks write permission; inode-record corruption after an unclean shutdown of the image.

Related errors


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