Hmbown/CodeWhale · error
; additionally failed to restore prior secret-store state…
Error message
{error}; additionally failed to restore prior secret-store state for {slot}: {rollback} What it means
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.
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.
Defensive patterns
Strategy: try-catch
Try / catch
match set_provider_api_key(provider, key) {
Err(e) if e.to_string().contains("failed to restore prior secret-store state") => {
eprintln!("Orphaned secret may remain in the keychain; restore or delete the slot manually.");
}
other => other?,
} Prevention
- 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
When it happens
Trigger: Calling `set_provider_api_key` where `store.save()` fails and the compensating `secrets.set(slot, previous)` (or `secrets.delete(slot)`) also returns an error.
Common situations: Secret backend went read-only or disconnected between the original write and the rollback — e.g. keychain re-locked mid-operation.
Related errors
- ; additionally could not verify secret-store rollback for
- Secret storage snapshot failed for
- Secret storage write failed for
- api_key cannot be empty string
- API key contains invalid control characters
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/90a04ba364bc69d1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/credentials.rs:139
"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.",
crate::quote_os_path(store.path())
));
}
};
if let Err(error) = store.save() {
store.config = original_config;
if secret_store_saved {
let current = secrets
.get(slot)
.map_err(|rollback| anyhow::anyhow!(
"{error}; additionally could not verify secret-store rollback for {slot}: {rollback}"
))?;
if current.as_deref() == Some(api_key) {
match prior_secret.expect("snapshot succeeded before secret write") {
Some(previous) => secrets.set(slot, &previous),
None => secrets.delete(slot),
}
.map_err(|rollback| anyhow::anyhow!(
"{error}; additionally failed to restore prior secret-store state for {slot}: {rollback}"
))?;
}
}
return Err(error);
}
crate::scrub_plaintext_api_keys_from_config_backup(store.path())
.context("failed to scrub plaintext API keys from config backup")?;
Ok(secret_store_saved)
}
/// What a credential clear actually accomplished.
///
/// The secret-store leg can fail after the config leg has already been
/// persisted. Reporting that separately is the point: a caller that prints
/// "cleared" while the key is still sitting in the keyring has lied about a
/// security-relevant action.
#[derive(Debug, Clone, PartialEq, Eq)]View on GitHub (pinned to 73e0f67d83)