{"record":{"id":"dc69dea3abff801d","repo":"Hmbown/CodeWhale","slug":"secret-storage-write-failed-for-slot-err-ref","errorCode":null,"errorMessage":"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.","messagePattern":"Secret storage write failed for (.+?): (.+?)\\. Refusing to write the API key in plaintext to (.+?)\\. Fix the configured secret backend and retry; Codewhale did not change that file\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/lib.rs","lineNumber":2357,"sourceCode":"    provider: ProviderKind,\n    api_key: &str,\n) -> Result<bool> {\n    let original_config = store.config.clone();\n    prepare_provider_api_key_metadata(store, provider);\n    let slot = provider_slot(provider);\n    // A readable prior value is required before a secret-store write so a\n    // later config failure can restore the exact prior state. If the backend\n    // cannot provide that snapshot, fail before changing the config file.\n    let prior_secret = secrets.get(slot);\n    let secret_store_saved = match prior_secret.as_ref().map_err(|error| error.to_string()) {\n        Ok(_) => match secrets.set(slot, api_key) {\n            Ok(()) => {\n                clear_provider_api_key_from_config(store, provider);\n                true\n            }\n            Err(err) => {\n                store.config = original_config;\n                return Err(anyhow::anyhow!(\n                    \"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.\",\n                    codewhale_config::quote_os_path(store.path())\n                ));\n            }\n        },\n        Err(error) => {\n            store.config = original_config;\n            return Err(anyhow::anyhow!(\n                \"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.\",\n                codewhale_config::quote_os_path(store.path())\n            ));\n        }\n    };\n    if let Err(error) = store.save() {\n        store.config = original_config;\n        if secret_store_saved {\n            let current = secrets\n                .get(slot)","sourceCodeStart":2339,"sourceCodeEnd":2375,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/cli/src/lib.rs#L2339-L2375","documentation":"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.","triggerScenarios":"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.","commonSituations":"Headless Linux servers or containers without gnome-keyring/kwallet; keyring locked before login completes; full or corrupted credential store.","solutions":["Unlock the keyring or start a secret service (gnome-keyring, kwallet) so writes succeed","Switch the configured secret backend to one available in that environment and retry","Rerun the same key-set command after the backend is fixed — the config was not modified, so the retry is clean","Confirm no plaintext key was written anywhere; the refusal is intentional"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Probe the secret backend with a throwaway slot before migrating real keys:\nfn secrets_backend_ok(secrets: &codewhale_secrets::Secrets) -> bool {\n    let probe = format!(\"__probe_{}\", std::process::id());\n    secrets.set(&probe, \"x\").is_ok() && { let ok = secrets.get(&probe).is_ok(); let _ = secrets.delete(&probe); ok }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure a secret service (gnome-keyring/kwallet) is running and unlocked on headless Linux before key setup","Run `codewhale doctor` to check secret-backend health before migrating keys","Never work around this by pre-writing the key into config as plaintext — the refusal exists to prevent that"],"tags":["cli","secrets","keyring","api-key","config","rust"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}