Hmbown/CodeWhale · error · anyhow::Error

Android loaded-image proof changed from {:?} to {:?}; refusi

Error message

Android loaded-image proof changed from {:?} to {:?}; refusing to replace the update target

What it means

validate_primary_update_identity re-derives the full Android executable proof (path, device, inode via dladdr + /proc/self/maps) immediately before replacing the update target and compares it to the proof captured when the update began. Any difference aborts the write, guarding against the loaded image having been swapped mid-update.

Source

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

    }
    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;
    let minor = ((device & 0xff) | ((device >> 12) & 0xfff00)) as u32;
    (major, minor)
}

fn validate_primary_update_identity(identity: &UpdateExecutableIdentity) -> Result<()> {
    #[cfg(target_os = "android")]
    {
        let fresh = android_loaded_executable_proof()?;
        if fresh != identity.android_proof {
            bail!(
                "Android loaded-image proof changed from {:?} to {:?}; refusing to replace the update target",
                identity.android_proof,
                fresh
            );
        }
        return Ok(());
    }

    #[cfg(not(target_os = "android"))]
    {
        let _ = identity;
        Ok(())
    }
}

fn replace_verified_downloads<F>(
    target_paths: &[PathBuf],
    verified_bytes: &[u8],

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Rerun the update after restarting the CLI so a fresh proof is captured
  2. Serialize self-updates (one at a time per install)
  3. Check that no package manager owns or rewrites the same path during the update window
Defensive patterns

Strategy: try-catch

Try / catch

Catch the proof-changed error at the final replace step, abort without writing, and instruct the user to rerun update after a restart; never catch-and-continue by forcing the write.

Prevention

When it happens

Trigger: Android self-update that reaches the final replace step after something changed the running image: a concurrent updater, a reinstall by a package manager, or the file being replaced between identity capture and replacement.

Common situations: Parallel update invocations, automation racing app-store updates, long-running sessions where an earlier update landed after this one started.

Related errors


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