Hmbown/CodeWhale · error
refusing to replace xAI OAuth generation
Error message
refusing to replace xAI OAuth generation
What it means
write_owned_file in XaiOAuthCredentialStore refuses to overwrite an existing generation file unless allow_replace is true. xAI OAuth tokens are stored as immutable generations with a pointer naming the live one; silently overwriting a generation would corrupt history and race with concurrent writers, so the store requires explicit intent (the pointer-switch path) before replacement.
Solutions
- Close other running instances of the app that share the credentials store, then retry the operation.
- Inspect the store directory for stale generation files from a failed persist; if the pointer names another generation, the stale file can be removed via logout/cleanup before re-authenticating.
- Log out (which runs cleanup removing only Codewhale-owned xAI files) and log back in to start a clean generation sequence.
- If writing via the library, pass allow_replace=true only on the explicit pointer-replacement path; never to work around the guard.
Example fix
// before
store.write_owned_file("token-gen-1.json", &bytes, false)?; // exists -> error
// after: write the new generation under a fresh name, then switch the pointer
store.write_owned_file("token-gen-2.json", &bytes, false)?;
store.switch_pointer("token-gen-2.json")?; Defensive patterns
Strategy: try-catch
Validate before calling
// Rust, before persisting a new generation
let target = store.directory().join(&generation_name);
if target.exists() {
// pick a fresh generation name or run logout cleanup first
} Try / catch
// match on the store error and recover by cleaning up + re-auth
match store.save_generation(&bytes) {
Err(e) if e.to_string().contains("refusing to replace") => {
store.logout_cleanup()?;
store.save_generation(&bytes)?;
}
r => r?,
} Prevention
- Run only one app instance that shares the xAI credentials store.
- After a failed persist, clean up stale generations (logout) before retrying.
- Never pass allow_replace=true outside the explicit pointer-switch code path.
When it happens
Trigger: Calling an internal store write for a generation basename that already exists on disk with allow_replace=false — e.g. saving a new token generation whose filename collides with an existing one (re-login producing the same generation name), or a stale pointer/retry re-attempting a completed write.
Common situations: Clock or counter reuse produced the same generation filename twice; a partially failed previous persist left the generation file in place and a retry hits the guard; running two app instances concurrently writing the same store.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- atomically replacing xAI OAuth credentials
- installing a new xAI OAuth generation without replacement
- restoring retired xAI OAuth file
- restoring retired xAI OAuth file
- agy OAuth token JSON carries no access token member
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/489b2b21db7b5145.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/xai_credentials.rs:1660
match File::open(self.directory.join(name)) {
Ok(file) => Ok(Some(file)),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(error) => Err(error.into()),
}
}
fn open_internal_file(&self, name: &str) -> Result<File> {
validate_private_basename(name)?;
Ok(fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(self.directory.join(name))?)
}
fn write_owned_file(&self, name: &str, bytes: &[u8], allow_replace: bool) -> Result<()> {
let path = self.directory.join(name);
anyhow::ensure!(
allow_replace || !path.exists(),
"refusing to replace xAI OAuth generation"
);
crate::persistence::atomic_write(&path, bytes)
}
fn remove_raw(&self, name: &str) -> Result<bool> {
validate_private_basename(name)?;
match fs::remove_file(self.directory.join(name)) {
Ok(()) => Ok(true),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false),
Err(error) => Err(error.into()),
}
}
fn rename_raw(&self, from: &str, to: &str) -> Result<()> {
validate_private_basename(from)?;
validate_private_basename(to)?;View on GitHub (pinned to 73e0f67d83)