{"record":{"id":"e504b94962b1497f","repo":"Hmbown/CodeWhale","slug":"secret-storage-operation-failed-failure-refu","errorCode":null,"errorMessage":"Secret storage {operation} failed: {failure}. 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 (.+?) failed: (.+?)\\. 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":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/config.rs","lineNumber":11982,"sourceCode":"                },\n                Err(error) => Err(plaintext_credential_fallback_refused(\n                    \"snapshot\", &path, &error,\n                )),\n            }\n        });\n    }\n\n    let path = save_api_key_to_config_file(trimmed)?;\n    codewhale_config::scrub_plaintext_api_keys_from_config_backup(&path)?;\n    Ok(SavedCredential::ConfigFile(path))\n}\n\nfn plaintext_credential_fallback_refused(\n    operation: &str,\n    config_path: &Path,\n    failure: &dyn std::fmt::Display,\n) -> anyhow::Error {\n    anyhow::anyhow!(\n        \"Secret storage {operation} failed: {failure}. 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(config_path)\n    )\n}\n\n/// The durable secret store for credential saves and logout-time deletes.\n///\n/// Under `cfg(test)` the store is only exposed when the test set both an\n/// isolated `CODEWHALE_HOME` and an explicit backend, so unit tests can never\n/// touch the developer's real credential store.\n#[cfg(not(test))]\npub(crate) fn credential_secret_store() -> Option<codewhale_secrets::Secrets> {\n    Some(codewhale_secrets::Secrets::auto_detect())\n}\n\n#[cfg(test)]\npub(crate) fn credential_secret_store() -> Option<codewhale_secrets::Secrets> {\n    let isolated_home = codewhale_paths::codewhale_home_is_explicit();","sourceCodeStart":11964,"sourceCodeEnd":12000,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/config.rs#L11964-L12000","documentation":"Refusal guard against silently downgrading to a plaintext credential file. When the configured secret backend (keyring) fails for a save/delete operation, Codewhale would otherwise fall back to writing the API key into the config file; this error aborts instead and explicitly does not touch the config file.","triggerScenarios":"Calling any credential save/delete path while the OS secret store is unavailable (keychain locked, `secret-service`/`keyring` crate backend error) and the code path that would otherwise write plaintext credentials to `config_path` is reached.","commonSituations":"Linux headless servers with no secret-service daemon; macOS keychain locked or access denied to the terminal app; Windows Credential Manager errors; keyring crate version changed its backend resolution.","solutions":["Fix or unlock the OS secret backend (start `gnome-keyring`/`kwallet`, unlock the macOS login keychain, grant the terminal Keychain access)","Retry the credential save once the backend works","Set up a usable keyring environment (e.g. run under a session with a secret service) rather than relying on the plaintext file","If credentials must be provided non-interactively, supply them via environment (`codex_credentials` env path) instead of the save routine"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let probe = keyring::Entry::new(\"codewhale\", \"probe\");\nif probe.and_then(|e| e.set_password(\"x\")).is_err() {\n    anyhow::bail!(\"secret backend unavailable; fix keyring before saving\");\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = save_api_key(key) {\n    if e.to_string().contains(\"Refusing to write the API key in plaintext\") {\n        // surface keyring setup instructions to the user\n    }\n}","preventionTips":["Ensure a keyring/secret-service daemon runs in the environment (headless CI: use dbus + gnome-keyring)","Unlock the OS keychain before automated credential operations","Never rely on plaintext config fallback for API keys"],"tags":["secrets","keyring","security","credentials"],"backgroundTag":"missing-credentials","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}