{"record":{"id":"90a04ba364bc69d1","repo":"Hmbown/CodeWhale","slug":"error-additionally-failed-to-restore-prior-secret-store","errorCode":null,"errorMessage":"{error}; additionally failed to restore prior secret-store state for {slot}: {rollback}","messagePattern":"(.+?); additionally failed to restore prior secret-store state for (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/credentials.rs","lineNumber":139,"sourceCode":"                \"Secret storage snapshot failed for {slot}: {error}. Refusing to write the API key in plaintext to {}. Fix the configured secret backend and retry; Codewhale did not change that file.\",\n                crate::quote_os_path(store.path())\n            ));\n        }\n    };\n    if let Err(error) = store.save() {\n        store.config = original_config;\n        if secret_store_saved {\n            let current = secrets\n                .get(slot)\n                .map_err(|rollback| anyhow::anyhow!(\n                    \"{error}; additionally could not verify secret-store rollback for {slot}: {rollback}\"\n                ))?;\n            if current.as_deref() == Some(api_key) {\n                match prior_secret.expect(\"snapshot succeeded before secret write\") {\n                    Some(previous) => secrets.set(slot, &previous),\n                    None => secrets.delete(slot),\n                }\n                .map_err(|rollback| anyhow::anyhow!(\n                    \"{error}; additionally failed to restore prior secret-store state for {slot}: {rollback}\"\n                ))?;\n            }\n        }\n        return Err(error);\n    }\n    crate::scrub_plaintext_api_keys_from_config_backup(store.path())\n        .context(\"failed to scrub plaintext API keys from config backup\")?;\n    Ok(secret_store_saved)\n}\n\n/// What a credential clear actually accomplished.\n///\n/// The secret-store leg can fail after the config leg has already been\n/// persisted. Reporting that separately is the point: a caller that prints\n/// \"cleared\" while the key is still sitting in the keyring has lied about a\n/// security-relevant action.\n#[derive(Debug, Clone, PartialEq, Eq)]","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/credentials.rs#L121-L157","documentation":"When the config save fails and rollback verification shows the slot still contains the new key, the code attempts to restore the snapshotted prior secret (`set` or `delete`). If that restore call fails, this error reports the restore failure alongside the original save error.","triggerScenarios":"Calling `set_provider_api_key` where `store.save()` fails and the compensating `secrets.set(slot, previous)` (or `secrets.delete(slot)`) also returns an error.","commonSituations":"Secret backend went read-only or disconnected between the original write and the rollback — e.g. keychain re-locked mid-operation.","solutions":["Inspect the secret slot in the backend: it may still hold the new key while the config was not saved.","Manually restore the previous secret value (or delete the slot) via the OS keychain tools.","Fix the underlying backend issue, then retry `set_provider_api_key`.","Treat the orphaned secret as stale and clean it up to avoid auth confusion."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match set_provider_api_key(provider, key) {\n    Err(e) if e.to_string().contains(\"failed to restore prior secret-store state\") => {\n        eprintln!(\"Orphaned secret may remain in the keychain; restore or delete the slot manually.\");\n    }\n    other => other?,\n}","preventionTips":["Keep the keychain unlocked for the whole operation to allow rollback writes","Record the prior secret slot state before scripted key rotation","Treat this error as requiring manual cleanup of the secret slot"],"tags":["secret-storage","rollback","atomicity","api-key"],"backgroundTag":"secret-store-rollback-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}