lima-vm/lima · error

inode %d value exceeds block boundary

Error message

inode %d value exceeds block boundary

What it means

chownInode found the inode's key in a leaf node, but computing the write location showed the inode value's uid/gid fields (valStart + inodeGroupOff + 4) would extend past the end of the block. This is a bounds check that prevents out-of-range writes; it means the stored value offset (vOff) is inconsistent with the block size.

Source

Thrown at pkg/apfs/chown.go:679

		if isRoot {
			valueAreaEnd -= btreeInfoSize
		}

		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)
			}

View on GitHub (pinned to dd909d0973)

Solutions

  1. Verify c.blockSize matches the apsb block_size field of the image
  2. Run fsck_apfs; a corrupt toc entry needs repair before any write
  3. Skip/abort the operation for this inode rather than writing out of bounds
  4. Check that the leaf's root flag handling (btreeInfoSize subtraction) matches the node being parsed
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the reader's blockSize matches the image
apsb, _ := readVolumeSuperblock(imagePath)
if c.blockSize != apsb.BlockSize {
    return fmt.Errorf("blockSize mismatch: reader %d, image %d", c.blockSize, apsb.BlockSize)
}

Type guard

func isBlockBoundaryError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "exceeds block boundary")
}

Try / catch

if err := c.chownInode(root, omap, maxXID, ino, uid, gid); isBlockBoundaryError(err) {
    return fmt.Errorf("value layout inconsistent; check blockSize/format: %w", err)
}

Prevention

When it happens

Trigger: A leaf node whose toc entry yields a value offset placing the inode value beyond blockSize — corrupt or foreign toc data, wrong blockSize assumption (e.g. image formatted with non-default block size the reader doesn't honor), or a root node's btreeInfo tail misaccounted.

Common situations: Reading images formatted with block sizes other than the assumed one; corrupted toc entries after fs damage; parsing a node type whose value layout differs.

Related errors


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