xai-org/grok-build · warning · std::io::Error

destination already exists

Error message

destination already exists

What it means

On Windows, atomic create-without-replace is emulated: atomic_rename_noreplace_windows first checks target.exists() and returns io::ErrorKind::AlreadyExists before renaming, so concurrent creation of the same workflow file fails fast instead of overwriting. It exists to guarantee that saving a brand-new workflow never clobbers an existing one.

Source

Thrown at crates/codegen/xai-grok-shell/src/session/workflow/registry.rs:645

        file.write_all(bytes)?;
        file.sync_all()?;
        #[cfg(unix)]
        std::fs::hard_link(&temp, target)?;
        #[cfg(windows)]
        atomic_rename_noreplace_windows(&temp, target)?;
        if let Ok(dir) = std::fs::File::open(parent) {
            let _ = dir.sync_all();
        }
        Ok(())
    })();
    let _ = std::fs::remove_file(&temp);
    result
}

#[cfg(windows)]
fn atomic_rename_noreplace_windows(source: &Path, target: &Path) -> io::Result<()> {
    if target.exists() {
        return Err(io::Error::new(
            io::ErrorKind::AlreadyExists,
            "destination already exists",
        ));
    }
    std::fs::rename(source, target)
}

#[derive(Debug, Clone, serde::Serialize)]
pub(crate) struct WorkflowListing {
    pub name: String,
    pub description: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub when_to_use: Option<String>,
    pub source: &'static str,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub path: Option<String>,
}

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Generate/use a fresh unique workflow id instead of reusing an existing one
  2. Check existence first and treat AlreadyExists as "already saved" if content is equivalent
  3. Use a read-modify-write or update path for existing workflows rather than create-new
  4. On Windows race-prone environments, serialize saves through a lock or the persistence channel

Example fix

// before
registry.save_project_workflow(&path, &bytes)?; // Err(AlreadyExists) on Windows
// after
if path.exists() {
    registry.update_project_workflow(&path, &bytes)?;
} else {
    registry.save_project_workflow(&path, &bytes)?;
}
Defensive patterns

Strategy: retry

Validate before calling

if cfg!(windows) && target.exists() {
    // treat as update rather than create-new
    return update_workflow(target, bytes);
}

Type guard

fn can_create_new(target: &Path) -> bool {
    !target.exists()
}

Try / catch

match registry.save_project_workflow(&target, &bytes) {
    Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
        // existing workflow: compare content or use update path
        registry.update_project_workflow(&target, &bytes)?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: atomic_create_new on Windows when the destination workflow file already exists — i.e. saving a project workflow whose file was already created (possibly by a concurrent writer or a previous run).

Common situations: Two processes/threads saving the same workflow id simultaneously on Windows; retrying a save after a timeout that actually succeeded; leftover file from a previous session with the same id.

Related errors


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