Hmbown/CodeWhale · error
refusing to replace an existing xAI OAuth generation
Error message
refusing to replace an existing xAI OAuth generation
What it means
Windows write_owned_file refuses to overwrite an existing generation when called with allow_replace=false (first-write semantics); the unix twin fails instead at linkat with EEXIST ("installing a new xAI OAuth generation without replacement"). The guard prevents a second login or refresh from silently clobbering credentials another flow just installed.
Source
Thrown at crates/config/src/xai_credentials.rs:956
})();
if let Err(error) = secured {
let cleanup = mark_windows_file_handle_for_deletion(&file);
return match cleanup {
Ok(()) => Err(error),
Err(cleanup) => Err(error).context(format!(
"also failed to delete the empty lifecycle lock: {cleanup:#}"
)),
};
}
return Ok(file);
}
bail!("xAI OAuth lifecycle lock changed repeatedly while opening")
}
fn write_owned_file(&self, name: &str, bytes: &[u8], allow_replace: bool) -> Result<()> {
let path = self.directory.join(name);
if let Some(existing) = self.open_owned_file_for_read(name)? {
anyhow::ensure!(
allow_replace,
"refusing to replace an existing xAI OAuth generation"
);
drop(existing);
}
let mut temporary = tempfile::NamedTempFile::new_in(&self.directory)
.context("creating private xAI OAuth temporary file")?;
let temporary_path = temporary.path().to_path_buf();
let security_handle =
reopen_windows_file_for_owner_security(temporary.as_file(), &temporary_path)?;
secure_windows_owner_only_handle(&security_handle, false)
.context("securing a new xAI OAuth temporary file before writing credentials")?;
validate_owned_file_handle(&security_handle, &temporary_path)
.context("verifying a new xAI OAuth temporary file before writing credentials")?;
let write_result = (|| -> Result<()> {
temporary
.write_all(bytes)
.context("writing xAI OAuth temporary file")?;View on GitHub (pinned to 8880682c63)
Solutions
- If updating existing credentials is intended (token refresh), pass allow_replace=true
- If it must be a fresh install, first store.remove(name) or pick a new generation id and repoint the config
- Check existence first: store.read_to_string(name)? returning Some means you must decide replace vs abort
- In tests, use a fresh CODEWHALE_HOME per run
Example fix
// before
store.write(generation, &serialized, false)?; // fails when it already exists
// after
if store.read_to_string(generation)?.is_some() {
store.write(generation, &serialized, true)?; // explicit refresh
} else {
store.write(generation, &serialized, false)?; // first install
} Defensive patterns
Strategy: validation
Validate before calling
// Decide replace semantics before writing
let exists = store.read_to_string(name)?.is_some();
anyhow::ensure!(
allow_replace || !exists,
"generation {name} already exists; pass allow_replace=true or pick a new id"
); Try / catch
match store.write(name, &bytes, false) {
Ok(()) => {}
Err(e) if e.to_string().contains("refusing to replace an existing xAI OAuth generation") => {
// deliberate refresh path
store.write(name, &bytes, true)?;
}
Err(e) => return Err(e),
} Prevention
- Pass allow_replace=true whenever refreshing tokens for a known generation
- Use a fresh generation id (and repoint config) for fresh installs
- Give each CI run its own CODEWHALE_HOME
When it happens
Trigger: store.write(name, bytes, false) when `name` already exists in $CODEWHALE_HOME/credentials: re-running codewhale auth xai-device against the same generation, a refresh racing an initial login, or tests replaying a write without clearing the directory.
Common situations: Retrying a partially failed device login; two concurrent logins converging on the same generation id; CI tests reusing one CODEWHALE_HOME across runs.
Related errors
- Codewhale credentials directory cannot be a volume root
- Codewhale-owned xAI OAuth path has the wrong filesystem type
- invalid Codewhale-owned xAI OAuth basename
- xAI OAuth private basename must be one UTF-8 path component
- xAI OAuth credentials directory must be absolute
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/e9ba9b0307ce9830.
Report an issue: GitHub.