Hmbown/CodeWhale · error · anyhow::Error

Android loaded-image identity changed: /proc/self/maps has d

Error message

Android loaded-image identity changed: /proc/self/maps has device/inode {:x}:{:x}:{}, but update target {} is {:x}:{:x}:{}; refusing to replace it

What it means

validate_android_mapping_identity stats the candidate update target and compares device major/minor plus inode against the /proc/self/maps row captured for the running image. A mismatch means the file at that path was replaced after the process started, so replacing it now could overwrite an unrelated file; the updater refuses (a deliberate TOCTOU guard).

Source

Thrown at crates/cli/src/update.rs:487

#[cfg(any(target_os = "android", all(test, unix)))]
fn validate_android_mapping_identity(
    mapping: &AndroidImageMapping,
    candidate: &Path,
) -> Result<()> {
    use std::os::unix::fs::MetadataExt;

    let candidate_metadata = std::fs::metadata(candidate).with_context(|| {
        format!(
            "failed to stat Android update target {}",
            candidate.display()
        )
    })?;
    let (candidate_major, candidate_minor) = android_device_parts(candidate_metadata.dev());
    let identity_matches = mapping.device_major == candidate_major
        && mapping.device_minor == candidate_minor
        && mapping.inode == candidate_metadata.ino();
    if !identity_matches {
        bail!(
            "Android loaded-image identity changed: /proc/self/maps has device/inode {:x}:{:x}:{}, but update target {} is {:x}:{:x}:{}; refusing to replace it",
            mapping.device_major,
            mapping.device_minor,
            mapping.inode,
            candidate.display(),
            candidate_major,
            candidate_minor,
            candidate_metadata.ino()
        );
    }
    Ok(())
}

#[cfg(any(target_os = "android", all(test, unix)))]
fn android_device_parts(device: u64) -> (u32, u32) {
    // Linux/Bionic's dev_t encoding, matching makedev(3), major(3), and
    // minor(3). `/proc/self/maps` renders these components in hexadecimal.
    let major = ((device >> 8) & 0xfff) as u32;

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Restart the CLI so it captures the new file's identity, then update again
  2. Run only one self-update at a time; serialize updates in scripts
  3. Avoid package-manager upgrades and self-update operating on the same install simultaneously
Defensive patterns

Strategy: try-catch

Try / catch

Catch the identity-mismatch error and prompt a restart of the CLI; a fresh process re-captures device/inode from the new file and the next update attempt passes.

Prevention

When it happens

Trigger: Android self-update while the underlying file changed since process start: a second updater ran concurrently, a package manager reinstalled the package, or the file was swapped by an installer between launch and update.

Common situations: Two codewhale update runs racing, pm/app-store upgrade racing a self-update, scripts that replace the binary while a long-lived session is open.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/cf8217554dabc24c. Report an issue: GitHub.