Hmbown/CodeWhale · error

; additionally could not verify secret-store rollback for

Error message

{error}; additionally could not verify secret-store rollback for {slot}: {rollback}

What it means

This error is raised in `persist_provider_api_key_unlocked` (crates/cli/src/lib.rs:2740) when persisting a provider API key: the secret store write succeeded, but the subsequent config-file save failed, and while attempting to roll back the secret store the verification read (`secrets.get(slot)`) itself failed. It chains the original config-save error with the rollback-read failure so the developer knows the secret slot may still hold the new key and its prior state could not be confirmed. The library throws it because it refuses to silently leave the secret store in an unknown state after a partial transaction.

Solutions

  1. Check the chained `{rollback}` message for the secret-backend root cause (keyring unavailable, dbus session missing, permission denied) and fix that backend first.
  2. Fix the original `{error}` (config save failure) — check permissions/ownership and free space on the config file path reported elsewhere in the output.
  3. Re-run the command after the backend is healthy; the persist flow is idempotent and will re-write the key.
  4. If the slot still holds the new key and that is acceptable, manually restore the prior secret (e.g. `secret-tool` / keyring utility) or re-run the previous provider's auth to overwrite it.

Example fix

// before: no preflight check, rollback read fails on a dead keyring
let secret_store_saved = match secrets.set(slot, api_key) { ... };
// after: verify the backend is readable before starting the transaction
secrets.get(slot).map_err(|e| anyhow!("secret backend unavailable: {e}"))?;
let secret_store_saved = match secrets.set(slot, api_key) { ... };
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: preflight the secret backend and config writability before the transaction
let prior = secrets.get(slot).map_err(|e| anyhow!("secret backend unreadable: {e}"))?;
let cfg_ok = store.path().parent().map(|p| p.exists()).unwrap_or(false);
if !cfg_ok { anyhow::bail!("config directory missing; fix it before persisting keys"); }

Try / catch

// Rust
match persist_provider_api_key(&mut store, &secrets, provider, &api_key) {
    Ok(saved_to_store) => { /* proceed; plaintext key was scrubbed */ }
    Err(e) if e.to_string().contains("could not verify secret-store rollback") => {
        // secret store state is unknown: inspect the slot with a keyring tool before retrying
        eprintln!("secret store in unknown state: {e:#}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Call the API-key persist flow (e.g. `codewhale` provider auth commands) where: (1) `secrets.set(slot, api_key)` succeeded, (2) `store.save()` returned Err (config file unwritable, disk full, permissions, invalid path), and then (3) `secrets.get(slot)` also returned Err during rollback verification.

Common situations: Headless/CI machines where the OS keyring (gnome-keyring, Windows Credential Manager, macOS Keychain) locked or the dbus session died between the set and the read; config file made read-only or its directory removed; disk-full during config save.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/c8f269318d0a24e1. Report an issue: GitHub.

Appendix: source

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

                    "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)
                .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);
    }
    codewhale_config::scrub_plaintext_api_keys_from_config_backup(store.path())?;
    Ok(secret_store_saved)
}

View on GitHub (pinned to 433685b202)