xai-org/grok-build · error

destination has no filename: {}

Error message

destination has no filename: {}

What it means

On Windows, windows_replace_exe derives the backup name (dest.old) from the destination's file name; if the destination path has no final component (e.g. a drive root like C:\ or a path ending in ..) it cannot form a backup name and throws this error. It guards the self-replacement logic from silently corrupting the install path.

Source

Thrown at crates/codegen/xai-grok-update/src/auto_update.rs:2081

///
/// On Windows the kernel prevents writes to a running executable but allows
/// renames. If a direct copy fails with a sharing violation, this renames
/// `dest` aside and copies `src` into the freed path. If the copy then
/// fails, the rename is rolled back to avoid a broken install.
///
/// The aside target is normally `<dest>.old`, but a leftover `.old` can
/// itself still be a running image (the session that was live during the
/// previous update keeps executing the renamed-aside file), and a running
/// image can neither be deleted nor rename-replaced. In that case `dest` is
/// renamed to a unique `<dest>.old.{pid}-{seq}.old` sibling instead, so a
/// locked leftover can never block the update. All `.old` leftovers are
/// swept best-effort at the start of each cycle; still-locked ones survive
/// until a later update runs after those processes exit.
#[cfg(windows)]
async fn windows_replace_exe(src: &std::path::Path, dest: &std::path::Path) -> Result<()> {
    let file_name = dest
        .file_name()
        .ok_or_else(|| anyhow::anyhow!("destination has no filename: {}", dest.display()))?
        .to_string_lossy();
    let old = dest.with_file_name(format!("{file_name}.old"));

    sweep_old_exe_backups(&old).await;

    match tokio::fs::copy(src, dest).await {
        Ok(_) => return Ok(()),
        // ERROR_SHARING_VIOLATION (32) / ERROR_ACCESS_DENIED (5): exe is
        // locked by a running process. Fall through to rename-and-replace.
        Err(e) if matches!(e.raw_os_error(), Some(32) | Some(5)) => {
            tracing::debug!("exe locked, falling back to rename: {e}");
        }
        Err(e) => return Err(e.into()),
    }

    // A .old that survived the sweep is locked; renaming onto it would need
    // to delete-replace it and fail, so divert to a guaranteed-free name.
    let old_is_free = matches!(

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Point the update destination at a concrete executable file path (e.g. C:\\Users\\me\\bin\\grok.exe), not a directory or root
  2. Normalize the path (canonicalize / dunce::canonicalize) so it resolves to a real file name
  3. Fix install_dir configuration so dest includes the executable file name

Example fix

// before
let dest = std::path::Path::new("C:\\>"); // drive root — no file name
windows_replace_exe(&src, dest).await?;
// after
let dest = std::path::Path::new("C:\\Users\\me\\bin\\grok.exe");
windows_replace_exe(&src, dest).await?;
Defensive patterns

Strategy: validation

Validate before calling

if dest.file_name().is_none() {
    anyhow::bail!("destination must be a file path, got: {}", dest.display());
}

Type guard

fn has_file_name(p: &std::path::Path) -> bool {
    p.file_name().map_or(false, |n| !n.is_empty())
}

Try / catch

match windows_replace_exe(&src, &dest).await {
    Err(e) if e.to_string().starts_with("destination has no filename") => {
        eprintln!("fix install_dir to point at the executable file");
    }
    result => result?,
}

Prevention

When it happens

Trigger: Calling windows_replace_exe (via the install/replace flow) with a dest path that has no file_name — drive roots ("C:\\"), trailing separators resolving to nothing, or empty/oddly-normalized paths.

Common situations: Misconfigured install directory pointing at a drive root; programmatically-built paths ending in a separator or ".."; a user config pointing the update destination at a mount root.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/026cf8fa7bf99f6d. Report an issue: GitHub.