{"record":{"id":"e9ba9b0307ce9830","repo":"Hmbown/CodeWhale","slug":"refusing-to-replace-an-existing-xai-oauth-generati","errorCode":null,"errorMessage":"refusing to replace an existing xAI OAuth generation","messagePattern":"refusing to replace an existing xAI OAuth generation","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/xai_credentials.rs","lineNumber":956,"sourceCode":"            })();\n            if let Err(error) = secured {\n                let cleanup = mark_windows_file_handle_for_deletion(&file);\n                return match cleanup {\n                    Ok(()) => Err(error),\n                    Err(cleanup) => Err(error).context(format!(\n                        \"also failed to delete the empty lifecycle lock: {cleanup:#}\"\n                    )),\n                };\n            }\n            return Ok(file);\n        }\n        bail!(\"xAI OAuth lifecycle lock changed repeatedly while opening\")\n    }\n\n    fn write_owned_file(&self, name: &str, bytes: &[u8], allow_replace: bool) -> Result<()> {\n        let path = self.directory.join(name);\n        if let Some(existing) = self.open_owned_file_for_read(name)? {\n            anyhow::ensure!(\n                allow_replace,\n                \"refusing to replace an existing xAI OAuth generation\"\n            );\n            drop(existing);\n        }\n        let mut temporary = tempfile::NamedTempFile::new_in(&self.directory)\n            .context(\"creating private xAI OAuth temporary file\")?;\n        let temporary_path = temporary.path().to_path_buf();\n        let security_handle =\n            reopen_windows_file_for_owner_security(temporary.as_file(), &temporary_path)?;\n        secure_windows_owner_only_handle(&security_handle, false)\n            .context(\"securing a new xAI OAuth temporary file before writing credentials\")?;\n        validate_owned_file_handle(&security_handle, &temporary_path)\n            .context(\"verifying a new xAI OAuth temporary file before writing credentials\")?;\n        let write_result = (|| -> Result<()> {\n            temporary\n                .write_all(bytes)\n                .context(\"writing xAI OAuth temporary file\")?;","sourceCodeStart":938,"sourceCodeEnd":974,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/config/src/xai_credentials.rs#L938-L974","documentation":"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.","triggerScenarios":"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.","commonSituations":"Retrying a partially failed device login; two concurrent logins converging on the same generation id; CI tests reusing one CODEWHALE_HOME across runs.","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"],"exampleFix":"// before\nstore.write(generation, &serialized, false)?; // fails when it already exists\n\n// after\nif store.read_to_string(generation)?.is_some() {\n    store.write(generation, &serialized, true)?; // explicit refresh\n} else {\n    store.write(generation, &serialized, false)?; // first install\n}","handlingStrategy":"validation","validationCode":"// Decide replace semantics before writing\nlet exists = store.read_to_string(name)?.is_some();\nanyhow::ensure!(\n    allow_replace || !exists,\n    \"generation {name} already exists; pass allow_replace=true or pick a new id\"\n);","typeGuard":null,"tryCatchPattern":"match store.write(name, &bytes, false) {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(\"refusing to replace an existing xAI OAuth generation\") => {\n        // deliberate refresh path\n        store.write(name, &bytes, true)?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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"],"tags":["windows","filesystem","concurrency","validation","xai-oauth"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}