lima-vm/lima · error
resolving child OID %d: %w
Error message
resolving child OID %d: %w
What it means
This error wraps a failure from omapLookup while descending an internal B-tree node in lookupDirEntry: the child block's virtual OID could not be translated to a physical address through the object map. The %d is the virtual OID and %w the underlying omap error (typically 'not found' or an omap node failure).
Source
Thrown at pkg/apfs/chown.go:585
// name hash to find the correct subtree.
childIdx := uint32(0)
for i := range nkeys {
kOff, _ := c.readTocEntry(blk, tocStart, i, isFixedKV)
keyStart := keyAreaStart + kOff
cmp := c.compareDrecKey(blk, keyStart, targetKeyHeader, name, targetHash)
if cmp <= 0 {
childIdx = i
} else {
break
}
}
// Read child OID (virtual) and resolve through omap.
_, vOff := c.readTocEntry(blk, tocStart, childIdx, isFixedKV)
childOID := le.Uint64(blk[valueAreaEnd-vOff:])
childPhys, err := c.omapLookup(omapTreeAddr, childOID, maxXID)
if err != nil {
return 0, fmt.Errorf("resolving child OID %d: %w", childOID, err)
}
blk, err = c.readBlock(childPhys)
if err != nil {
return 0, err
}
}
}
// readDrecName reads a directory record name from the key data
// starting at nameFieldOff. Handles both hashed (j_drec_hashed_key_t,
// 4-byte name_len_and_hash) and non-hashed (j_drec_key_t, 2-byte
// name_len) formats.
func (c *container) readDrecName(blk []byte, nameFieldOff uint32) string {
// Heuristic: if the 4-byte field at nameFieldOff has its upper
// bits set (hash), it's a hashed key. For non-hashed keys, the
// 2-byte name_len is followed by the name bytes.
//
// In j_drec_hashed_key_t: name_len_and_hash (uint32) whereView on GitHub (pinned to dd909d0973)
Solutions
- Use the max XID (latest transaction) consistent with the tree you are walking
- Pass the omap tree address belonging to the same volume/superblock as fsRootPhys
- Run fsck_apfs to repair a damaged omap
- Re-read the container superblock and re-resolve both tree roots before walking
Defensive patterns
Strategy: retry
Validate before calling
// Use the latest transaction ID so omap entries exist
maxXID := readContainerMaxXID(containerSuperblock)
if maxXID == 0 {
return errors.New("invalid maxXID: use latest container transaction")
} Type guard
func isOmapResolutionError(err error) bool {
return err != nil && strings.Contains(err.Error(), "resolving child OID")
} Try / catch
inode, err := c.resolvePath(root, omap, maxXID, path)
if isOmapResolutionError(err) {
// re-read superblock for fresh tree roots and retry once
fsRoot, omapTree, maxXID = reResolveRoots()
inode, err = c.resolvePath(fsRoot, omapTree, maxXID, path)
} Prevention
- Always re-read the container superblock immediately before a walk
- Match snapshot tree with the snapshot's omap, live tree with live omap
- Never use a lower maxXID than the generation of the tree you walk
When it happens
Trigger: resolvePath descends into a child node whose virtual OID is absent from the omap tree for the given maxXID — the child was created after maxXID (stale transaction snapshot), the wrong omap tree address was supplied, or the omap entry was deleted by a rebalance/cleanup.
Common situations: Reading an old snapshot (maxXID too low) while the live tree was modified; passing the live omap root to a snapshot tree walk; corrupted or mismatched container superblock selection on multi-volume images.
Related errors
- omap entry for OID %d not found
- filesystem B-tree node checksum failed
- filesystem B-tree node: %w
- directory entry %#q not found
- inode %d not found in filesystem B-tree
AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01).
Data as JSON: /api/errors/d17976d7c0b80097.
Report an issue: GitHub.