xai-org/grok-build · error

failed to replace {}: {e}

Error message

failed to replace {}: {e}

What it means

`write_json_atomic` finishes by `std::fs::rename(tmp, path)` to atomically replace auth.json. This error is thrown when the rename fails; the temp file is cleaned up first. Typical wrapped io::Errors are cross-device link errors, permission problems on the destination, or on Windows the destination being locked by another process.

Source

Thrown at crates/codegen/xai-grok-workspace/src/hub_auth/mod.rs:374

    #[cfg(unix)]
    {
        use std::os::unix::fs::OpenOptionsExt;
        opts.mode(0o600);
    }

    let mut file = opts
        .open(&tmp)
        .map_err(|e| anyhow::anyhow!("failed to open {}: {e}", tmp.display()))?;
    file.write_all(json.as_bytes())?;
    file.sync_all()?;
    drop(file);

    #[cfg(windows)]
    let _ = std::fs::remove_file(path);

    if let Err(e) = std::fs::rename(&tmp, path) {
        let _ = std::fs::remove_file(&tmp);
        return Err(anyhow::anyhow!("failed to replace {}: {e}", path.display()));
    }
    Ok(())
}

/// Build a hub auth provider for `hub_url`. `auth_config` overrides
/// the default credential path (`~/.grok/auth.json`).
///
/// `refresh_cfg.enabled` selects the workspace-owned proactive refresher;
/// when off (the default) this is the SDK `OidcAuthProvider`. Loopback
/// `ws://` ignores the flag and stays on a static bearer.
pub fn provider(
    hub_url: &Url,
    auth_config: Option<&Path>,
    refresh_cfg: &ProactiveRefreshConfig,
) -> anyhow::Result<Arc<dyn AuthProvider>> {
    let auth_path = match auth_config {
        Some(p) => p.to_path_buf(),
        None => default_auth_path()?,

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Check write permissions on the directory and existing auth.json (`ls -ld ~/.grok ~/.grok/auth.json`).
  2. Retry the refresh — the atomic rename is designed to be safe to repeat; transient locks usually clear.
  3. Ensure tmp and auth.json live on the same filesystem (do not symlink ~/.grok across mounts).
  4. On Windows, close tools holding auth.json open (editors, sync clients) and exclude the directory from real-time AV scanning.
Defensive patterns

Strategy: retry

Try / catch

// Rename failures are often transient (locks, concurrent refresh); retry with backoff
for attempt in 0..3 {
    match write_refreshed_token(&auth_path, &scope_key, &event) {
        Ok(()) => break,
        Err(e) if attempt < 2 && e.to_string().contains("failed to replace") => {
            std::thread::sleep(std::time::Duration::from_millis(100 * (attempt + 1)));
        }
        Err(e) => { tracing::warn!(error = %e, "token persist failed"); break; }
    }
}

Prevention

When it happens

Trigger: `write_refreshed_token` -> `write_json_atomic` when `fs::rename(&tmp, path)` returns Err: destination directory not writable, destination file locked (Windows), tmp and destination on different filesystems, or destination removed/replaced concurrently by another process.

Common situations: Two grok processes refreshing simultaneously and one deletes/locks the file; ~/.grok on a different mount than a symlinked temp dir; Windows Defender briefly holding auth.json; read-only home mount.

Related errors


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