jdx/mise · critical
{target} on '{}' changed after it was installed; another wri
Error message
{target} on '{}' changed after it was installed; another writer owns that path What it means
After installing mise on the remote host, mise computes the SHA-256 of the installed executable and compares it with the digest of what it just wrote. A mismatch means something else rewrote the install path between write and verification, so mise refuses to run an executable it cannot trust.
Source
Thrown at src/system/remote.rs:1080
if remote_executable_sha256(session, target)
.await?
.is_some_and(|installed| installed == expected)
{
info!(
"mise is already installed at {target} on {}",
session.host.name
);
return Ok(());
}
let file = File::open(binary)
.wrap_err_with(|| format!("failed to open mise binary {}", binary.display()))?;
session
.status_with_stdin_async(&["sh", "-c", &install_mise_script(target)], file)
.await?;
// An install path can be writable by more than the SSH account, so confirm
// the executable about to run is the one that was just written.
match remote_executable_sha256(session, target).await? {
Some(installed) if installed != expected => bail!(
"{target} on '{}' changed after it was installed; another writer owns that path",
session.host.name
),
Some(_) => {}
None => warn!(
"cannot verify the mise installed at {target} on {}; the host returned no SHA-256 digest",
session.host.name
),
}
info!("installed mise to {target} on {}", session.host.name);
Ok(())
}
/// Writes beside the target and renames, so a replaced executable is never
/// truncated in place and a busy binary cannot fail with ETXTBSY. The write
/// goes into a `mktemp -d` directory rather than a named sibling file: that
/// directory is private to the SSH account, so nobody else can swap the path
/// out from under `cat` in a shared writable install directory. A directory atView on GitHub (pinned to afd2eddd3a)
Solutions
- Install to a user-owned path (e.g. ~/.local/bin/mise) only writable by the SSH account
- Stop concurrent processes/jobs that also write to that install path
- Re-run the install; if it keeps failing, investigate who else owns the path (ls -la, chattr)
- Tighten permissions on the target directory so only the install account can write
Example fix
// before: shared writable path MISE_INSTALL_PATH=/usr/local/bin/mise // after: account-private path MISE_INSTALL_PATH=$HOME/.local/bin/mise
Defensive patterns
Strategy: try-catch
Validate before calling
const expectedSha = crypto.createHash('sha256').update(fs.readFileSync(localMiseBinary)).digest('hex');
// ensure remote install dir is owned and writable only by the SSH account before install Try / catch
try { await installRemoteMise(); } catch (e) { if (String(e).includes('changed after it was installed')) { quarantinePath(target); await installRemoteMise(); } throw e; } Prevention
- Install to account-private paths (~/.local/bin) not shared dirs
- Serialize installs so no concurrent writer touches the path
- Re-verify the SHA-256 after any path-sharing change on the host
When it happens
Trigger: remote_executable_sha256 returns a digest different from the expected one right after status_with_stdin_async ran install_mise_script(target) — i.e. the file at target was replaced or modified by another writer on the host.
Common situations: Shared multi-user hosts where MISE_INSTALL_PATH (e.g. /usr/local/bin/mise) is writable by other accounts or by another concurrent mise bootstrap; CI runners with a pre-seeded mise at the same path.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- automatic cross-platform provisioning refuses to replace a c
- brew-cask:{}: cask metadata has no sha256
- verified checksum file digest does not match expected checks
- Checksum mismatch for file {}: Expected: {algo}:{checksum} A
- path component {} was concurrently created by another user
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/2d7924009c9fb9ee.
Report an issue: GitHub.