Hmbown/CodeWhale · error

Refusing to replace : the update target is not a regular…

Error message

Refusing to replace {}: the update target is not a regular file.

{GITHUB_MIGRATION_HELP}

What it means

The self-updater validates that the resolved update target is a regular file before replacing it. If symlink_metadata shows the target is a symlink, directory, or other non-regular file, it bails rather than clobbering whatever sits at that path. This prevents replacing launchers, aliases, or directories by mistake.

Solutions

  1. Replace the symlink/wrapper with a real copy of the codewhale binary at the target path, then re-run update.
  2. Point PATH at the directory containing the actual regular-file binary and run update from there.
  3. Follow GITHUB_MIGRATION_HELP to reinstall cleanly into a supported location.

Example fix

// before
$ ln -s /opt/codewhale/bin/codewhale ~/.local/bin/codewhale && codewhale update  # refused
// after
$ cp /opt/codewhale/bin/codewhale ~/.local/bin/codewhale && codewhale update
Defensive patterns

Strategy: validation

Validate before calling

fn is_regular_file(p: &std::path::Path) -> bool {
    matches!(std::fs::symlink_metadata(p), Ok(m) if m.is_file() && !m.is_symlink())
}

Type guard

fn is_regular_non_symlink(md: &std::fs::Metadata) -> bool {
    md.is_file() && !md.is_symlink()
}

Prevention

When it happens

Trigger: run_update resolves a target path that is a symlink (even to the correct binary), a directory, or a device/socket; validate_update_target's `!metadata.is_file() || metadata.is_symlink()` check fires.

Common situations: User created a symlink named `codewhale` in ~/.local/bin pointing elsewhere; the install 'binary' is actually a wrapper script directory layout; the target was replaced by a desktop entry.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/0f9a1ba3f3c8cf40. Report an issue: GitHub.

Appendix: source

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

fn validate_update_target(target: &Path, identity: &UpdateExecutableIdentity) -> Result<()> {
    if !InstallMethod::from_path(target).supports_self_update() || protected_update_path(target) {
        bail!(
            "Refusing to replace managed/system path {}.\n\n{GITHUB_MIGRATION_HELP}",
            target.display()
        );
    }
    let metadata = match std::fs::symlink_metadata(target) {
        Ok(metadata) => metadata,
        Err(error) if error.kind() == std::io::ErrorKind::NotFound && target != identity.path => {
            return Ok(());
        }
        Err(error) => {
            return Err(error)
                .with_context(|| format!("failed to inspect update target {}", target.display()));
        }
    };
    if !metadata.is_file() || metadata.is_symlink() {
        bail!(
            "Refusing to replace {}: the update target is not a regular file.\n\n{GITHUB_MIGRATION_HELP}",
            target.display()
        );
    }
    let bytes = std::fs::read(target)
        .with_context(|| format!("failed to identify update target {}", target.display()))?;
    if sha256_hex(&bytes) != identity.file_hash {
        bail!(
            "Refusing to replace {}: its bytes differ from the running executable. This may be another installation or an unrelated command. No command is removed automatically.\n\n{GITHUB_MIGRATION_HELP}",
            target.display()
        );
    }
    Ok(())
}

fn protected_update_path(path: &Path) -> bool {
    [
        "/usr/bin",

View on GitHub (pinned to 73e0f67d83)