{"record":{"id":"2d7924009c9fb9ee","repo":"jdx/mise","slug":"target-on-changed-after-it-was-installed-a","errorCode":null,"errorMessage":"{target} on '{}' changed after it was installed; another writer owns that path","messagePattern":"(.+?) on '(.+?)' changed after it was installed; another writer owns that path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/system/remote.rs","lineNumber":1080,"sourceCode":"    if remote_executable_sha256(session, target)\n        .await?\n        .is_some_and(|installed| installed == expected)\n    {\n        info!(\n            \"mise is already installed at {target} on {}\",\n            session.host.name\n        );\n        return Ok(());\n    }\n    let file = File::open(binary)\n        .wrap_err_with(|| format!(\"failed to open mise binary {}\", binary.display()))?;\n    session\n        .status_with_stdin_async(&[\"sh\", \"-c\", &install_mise_script(target)], file)\n        .await?;\n    // An install path can be writable by more than the SSH account, so confirm\n    // the executable about to run is the one that was just written.\n    match remote_executable_sha256(session, target).await? {\n        Some(installed) if installed != expected => bail!(\n            \"{target} on '{}' changed after it was installed; another writer owns that path\",\n            session.host.name\n        ),\n        Some(_) => {}\n        None => warn!(\n            \"cannot verify the mise installed at {target} on {}; the host returned no SHA-256 digest\",\n            session.host.name\n        ),\n    }\n    info!(\"installed mise to {target} on {}\", session.host.name);\n    Ok(())\n}\n\n/// Writes beside the target and renames, so a replaced executable is never\n/// truncated in place and a busy binary cannot fail with ETXTBSY. The write\n/// goes into a `mktemp -d` directory rather than a named sibling file: that\n/// directory is private to the SSH account, so nobody else can swap the path\n/// out from under `cat` in a shared writable install directory. A directory at","sourceCodeStart":1062,"sourceCodeEnd":1098,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/remote.rs#L1062-L1098","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: shared writable path\nMISE_INSTALL_PATH=/usr/local/bin/mise\n// after: account-private path\nMISE_INSTALL_PATH=$HOME/.local/bin/mise","handlingStrategy":"try-catch","validationCode":"const expectedSha = crypto.createHash('sha256').update(fs.readFileSync(localMiseBinary)).digest('hex');\n// ensure remote install dir is owned and writable only by the SSH account before install","typeGuard":null,"tryCatchPattern":"try { await installRemoteMise(); } catch (e) { if (String(e).includes('changed after it was installed')) { quarantinePath(target); await installRemoteMise(); } throw e; }","preventionTips":["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"],"tags":["security","checksum","remote","ssh","concurrency"],"backgroundTag":"checksum-mismatch","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}