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

If the config save fails after the secret was written, the code verifies the secret store was rolled back to its prior value by re-reading the slot. When that verification read fails, this error layers the rollback-read failure onto the original save error so the caller knows the secret-store state is unverified.

Solutions

  1. Read both embedded errors: the first is the config-save failure, the second the rollback-verification failure.
  2. Fix the config file write problem (permissions, disk space, read-only mount) first.
  3. Manually inspect the secret slot in the backend and restore the prior value if it still holds the new key.
  4. Retry the operation once both the filesystem and secret backend are healthy.
Defensive patterns

Strategy: try-catch

Try / catch

match set_provider_api_key(provider, key) {
    Err(e) if e.to_string().contains("could not verify secret-store rollback") => {
        eprintln!("Config save failed AND rollback unverifiable. Inspect the keychain slot manually.");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `set_provider_api_key` where `store.save()` fails AND the subsequent `secrets.get(slot)` used to confirm rollback also errors.

Common situations: Config file became unwritable (permissions, disk full) at the same time the secret backend turned flaky — e.g. keychain session dropped mid-operation.

Related errors


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

Appendix: source

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

                    "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)
                .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)
}

View on GitHub (pinned to 73e0f67d83)