Hmbown/CodeWhale · error · anyhow::Error

Secret storage write failed for {slot}: {err}. Refusing to w

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

During migration of a provider API key from plaintext config into the secret store, the write secrets.set(slot, api_key) failed. Codewhale refuses the plaintext fallback by design: the in-memory config is restored and the error names the slot, the backend error, and the config path that was deliberately left unchanged.

Source

Thrown at crates/cli/src/lib.rs:2357

    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.",
                    codewhale_config::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.",
                codewhale_config::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 8880682c63)

Solutions

  1. Unlock the keyring or start a secret service (gnome-keyring, kwallet) so writes succeed
  2. Switch the configured secret backend to one available in that environment and retry
  3. Rerun the same key-set command after the backend is fixed — the config was not modified, so the retry is clean
  4. Confirm no plaintext key was written anywhere; the refusal is intentional
Defensive patterns

Strategy: validation

Validate before calling

// Probe the secret backend with a throwaway slot before migrating real keys:
fn secrets_backend_ok(secrets: &codewhale_secrets::Secrets) -> bool {
    let probe = format!("__probe_{}", std::process::id());
    secrets.set(&probe, "x").is_ok() && { let ok = secrets.get(&probe).is_ok(); let _ = secrets.delete(&probe); ok }
}

Prevention

When it happens

Trigger: Provider auth/key-set flows when the OS secret backend errors on write: locked keyring, missing secret-service/dbus on headless Linux, Windows Credential Manager unavailable, or a configured backend (CODEWHALE_SECRETS_BACKEND) that cannot persist.

Common situations: Headless Linux servers or containers without gnome-keyring/kwallet; keyring locked before login completes; full or corrupted credential store.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/dc69dea3abff801d. Report an issue: GitHub.