ClementTsang/bottom · error · anyhow::Error

IORegistryEntryGetParentEntry failed, error code

Error message

IORegistryEntryGetParentEntry failed, error code {result}.

What it means

service_parent walks up the IOKit service plane (kIOServicePlane) via IORegistryEntryGetParentEntry to reach the parent of an IOKit entry (e.g. from IOSBlockStorageService up to its driver). A non-KERN_SUCCESS result is converted into this error.

Solutions

  1. Treat missing parents as a traversal boundary and stop walking up for that entry
  2. Re-run disk enumeration to get fresh, valid IOKit entries
  3. Inspect the kern_return_t code to distinguish 'no parent' from invalid entry
  4. Skip that device's stats instead of failing the whole harvest

Example fix

// before
let parent = entry.service_parent()?;
// after
let parent = match entry.service_parent() {
    Ok(p) => p,
    Err(e) => { log::debug!("no parent entry, stopping traversal: {e}"); return None },
};
Defensive patterns

Strategy: try-catch

Try / catch

let parent = match entry.service_parent() {
    Ok(p) => Some(p),
    Err(e) if e.to_string().contains("IORegistryEntryGetParentEntry") => {
        log::debug!("traversal boundary: {e}"); None
    },
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Calling service_parent() on an io_object that has no parent in the IOService plane or is not a valid registry entry — typically during the disk statistics traversal.

Common situations: Hot-unplugged devices mid-enumeration, synthetic/edge entries lacking parents, or entries from a stale iterator after system changes.

Related errors


AI-assisted analysis of ClementTsang/bottom@b77d317502 (2026-09-07). Data as JSON: /api/errors/b32c2e87f2fa133d. Report an issue: GitHub.

Appendix: source

Thrown at src/collection/disks/unix/macos/io_kit/io_object.rs:60

            } else {
                let props = props.assume_init();
                Ok(CFMutableDictionary::wrap_under_create_rule(props).to_immutable())
            }
        }
    }

    /// Gets the [`kIOServicePlane`] parent [`io_object_t`] for this
    /// [`io_object_t`], if there is one.
    pub fn service_parent(&self) -> anyhow::Result<IoObject> {
        let mut parent: io_registry_entry_t = 0;

        // SAFETY: IOKit call, the arguments should be safe.
        let result = unsafe {
            IORegistryEntryGetParentEntry(self.0, kIOServicePlane.as_ptr().cast(), &mut parent)
        };

        if result != kern_return::KERN_SUCCESS {
            bail!("IORegistryEntryGetParentEntry failed, error code {result}.")
        } else {
            Ok(parent.into())
        }
    }

    // pub fn conforms_to_block_storage_driver(&self) -> bool {
    //     // SAFETY: IOKit call, the arguments should be safe.
    //     let result =
    //         unsafe { IOObjectConformsTo(self.0,
    // "IOBlockStorageDriver\0".as_ptr().cast()) };

    //     result != 0
    // }
}

impl From<io_object_t> for IoObject {
    fn from(obj: io_object_t) -> IoObject {
        IoObject(obj)

View on GitHub (pinned to b77d317502)