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
- Check write permissions on the directory and existing auth.json (`ls -ld ~/.grok ~/.grok/auth.json`).
- Retry the refresh — the atomic rename is designed to be safe to repeat; transient locks usually clear.
- Ensure tmp and auth.json live on the same filesystem (do not symlink ~/.grok across mounts).
- 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
- Don't run multiple refresh-capable clients against the same GROK_HOME concurrently.
- Keep ~/.grok and its temp files on the same filesystem — no cross-mount symlinks.
- On Windows, exclude auth.json from real-time scanners that hold files open during rename.
- Remember a failed persist only loses the newest token; the in-memory access token still works until expiry.
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
- failed to read {}: {e}
- failed to open {}: {e}
- InvalidInput
- journal is not a regular file: {}
- journal exceeds {MAX_JOURNAL_BYTES} bytes
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/7a99fb00c6794024.
Report an issue: GitHub.