Hmbown/CodeWhale · error

Secret storage write failed for

Error message

Secret storage write failed for {slot}: {err}. Refusing to write the API key in plaintext to {}. Fix the configured secret backend and retry; Codewhale did not change that file.

What it means

`set_provider_api_key_unlocked` first writes the key to the configured OS secret backend (keychain/credential manager). If that write fails, it restores the original in-memory config and refuses with this error — the API key is never downgraded to plaintext in the config file. The message names the secret slot, the backend error, and the untouched config path.

Solutions

  1. Unlock or start the OS secret backend (unlock Keychain; start gnome-keyring/keyring daemon; log into the desktop session).
  2. Retry `set_provider_api_key` after the backend is healthy — the config file was left unchanged.
  3. Inspect the backend error embedded in the message for access-denied vs unavailable causes.
  4. If on headless Linux, install/initialize a secret service provider (e.g. `gnome-keyring` or use a file-backed keyring the tool supports).

Example fix

// before (headless Linux, no keyring)
$ codewhale auth set openai sk-...
Error: Secret storage write failed ...
// after
$ eval $(gnome-keyring-daemon --start --components=secrets)
$ codewhale auth set openai sk-...
Defensive patterns

Strategy: try-catch

Try / catch

match set_provider_api_key(provider, key) {
    Err(e) if e.to_string().starts_with("Secret storage write failed") => {
        eprintln!("Unlock your OS keychain / start the secret service, then retry. Config file was not modified.");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `set_provider_api_key` when the OS secret store's `set(slot, key)` returns an error (keychain locked, service unavailable, access denied).

Common situations: macOS Keychain locked or denied access, Linux secret service (gnome-keyring/KWallet) not running over SSH, Windows Credential Manager policy restrictions.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/894e8621ac0f5482. Report an issue: GitHub.

Appendix: source

Thrown at crates/config/src/credentials.rs:112

    provider: ProviderKind,
    api_key: &str,
) -> Result<bool> {
    let original_config = store.config.clone();
    prepare_provider_api_key_metadata(store, provider);
    let slot = provider_slot(provider);
    // A readable prior value is required before a secret-store write so a
    // later config failure can restore the exact prior state. If the backend
    // cannot provide that snapshot, fail before changing the config file.
    let prior_secret = secrets.get(slot);
    let secret_store_saved = match prior_secret.as_ref().map_err(|error| error.to_string()) {
        Ok(_) => match secrets.set(slot, api_key) {
            Ok(()) => {
                clear_provider_api_key_from_config(store, provider);
                true
            }
            Err(err) => {
                store.config = original_config;
                return Err(anyhow::anyhow!(
                    "Secret storage write failed for {slot}: {err}. 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())
                ));
            }
        },
        Err(error) => {
            store.config = original_config;
            return Err(anyhow::anyhow!(
                "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)

View on GitHub (pinned to 73e0f67d83)