Hmbown/CodeWhale · error
staged update is not executable (mode ); refusing to replace
Error message
staged update {} is not executable (mode {:03o}); refusing to replace {} What it means
Thrown during the atomic install step of self-update. After staging the new binary to a temp file, the updater checks that the staged file has any execute permission bit (0o111); if not (mode bits shown in octal), it refuses to replace the current binary rather than installing an unrunnable executable.
Solutions
- Re-run the update so the staged binary is written with mode 0755 (set executable bits on extraction).
- Fix the extraction code to apply Unix permissions from the archive entries.
- Check the release archive: confirm the binary entry has executable permissions and rebuild it if not.
- Ensure the target filesystem supports execute permissions (not mounted noexec).
Example fix
// before fs::write(&staged_path, bytes)?; // after fs::write(&staged_path, bytes)?; let mut perms = fs::metadata(&staged_path)?.permissions(); use std::os::unix::fs::PermissionsExt; perms.set_mode(0o755); fs::set_permissions(&staged_path, perms)?;
Defensive patterns
Strategy: validation
Validate before calling
use std::os::unix::fs::PermissionsExt;
let mode = std::fs::metadata(&staged)?.permissions().mode();
if mode & 0o111 == 0 {
eprintln!("staged binary {staged:?} not executable (mode {mode:03o}); fix extraction or mount", );
return Err(anyhow!("staged binary not executable"));
} Prevention
- Set mode 0755 on the staged binary right after extraction.
- Preserve Unix permission bits when unpacking archives (use a tar/zip lib that reads external attributes).
- Ensure the target filesystem is not mounted noexec.
- Test the update path on each OS you ship.
When it happens
Trigger: Self-update reaches the persist step but the extracted/staged temp binary lacks execute bits — typically when the archive stored the file mode 0644 or the extraction lost permissions.
Common situations: Extraction tool or code copied the file without preserving the mode bit; extracting on a filesystem that does not support Unix permissions; a packaging bug in the release archive.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Android loaded image
- Codewhale-owned credential file must be singly linked…
- failed to install new binary at
- Failed to read MCP config
- failed to remove
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/149b236e37f63bce.
Report an issue: GitHub.
Appendix: source
Thrown at crates/cli/src/update.rs:1893
// Independently verify the staged binary is executable before it may
// replace the target; a chmod that silently did not stick would otherwise
// install a binary that cannot run.
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let staged_mode = tmp
.as_file()
.metadata()
.with_context(|| {
format!(
"failed to inspect staged update at {}",
tmp.path().display()
)
})?
.permissions()
.mode();
if staged_mode & 0o111 == 0 {
bail!(
"staged update {} is not executable (mode {:03o}); refusing to replace {}",
tmp.path().display(),
staged_mode & 0o7777,
target.display()
);
}
}
validate_before_replace()?;
#[cfg(windows)]
{
let backup = backup_path_for(target);
if target.exists() {
std::fs::rename(target, &backup).with_context(|| {
format!(
"failed to move current executable {} to {}",
target.display(),View on GitHub (pinned to 73e0f67d83)