{"record":{"id":"cedf422725c4a044","repo":"gitbutlerapp/gitbutler","slug":"unused","errorCode":null,"errorMessage":"unused","messagePattern":"unused","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-secret/src/secret.rs","lineNumber":256,"sourceCode":"                .store\n                .credentials(&self.handle, None)\n                .map_err(|err| keyring::Error::PlatformFailure(err.into()))?;\n            match cascade.invoke(get_action, prompt) {\n                Ok(Some(out)) => Ok(out.identity.password),\n                Ok(None) => Err(keyring::Error::NoEntry),\n                Err(err) => {\n                    tracing::debug!(err = ?err, \"credential-helper invoke failed - usually this means it wanted to prompt which is disabled\");\n                    Err(keyring::Error::NoEntry)\n                }\n            }\n        }\n\n        fn as_any(&self) -> &dyn Any {\n            self\n        }\n\n        fn set_secret(&self, _password: &[u8]) -> keyring::Result<()> {\n            unreachable!(\"unused\")\n        }\n\n        fn get_secret(&self) -> keyring::Result<Vec<u8>> {\n            unreachable!(\"unused\")\n        }\n\n        #[instrument(skip(self), err(Debug))]\n        fn delete_credential(&self) -> keyring::Result<()> {\n            let (mut cascade, action, prompt) = self\n                .store\n                .credentials(&self.handle, None)\n                .map_err(|err| keyring::Error::PlatformFailure(err.into()))?;\n            let ctx = action.context().expect(\"available for get\").to_owned();\n            let action = gix::credentials::helper::NextAction::from(ctx).erase();\n            cascade\n                .invoke(action, prompt)\n                .map_err(|err| keyring::Error::PlatformFailure(err.into()))?;\n            Ok(())","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-secret/src/secret.rs#L238-L274","documentation":"but-secret implements a custom keyring CredentialStore that drives git's credential cascade (gix::credentials helper) for get/delete flows. `set_secret` is never invoked by keyring in that flow, so it is wired to `unreachable!(\"unused\")`. Calling it means code tried to programmatically write a credential into a store that only supports prompt-based get and cascade-driven delete (crates/but-secret/src/secret.rs:256).","triggerScenarios":"Invoking `keyring::Entry::set_password`/`set_secret` on an Entry backed by this cascade store - e.g. new code that persists a token instead of letting the credential helper prompt, or a test harness that exercises every trait method.","commonSituations":"Adding a login/pat-save feature that writes to the keyring directly; porting code from a different keyring backend that did support set; test suites iterating over store capabilities.","solutions":["Do not call set on entries from this store; go through the gix credentials cascade/prompt flow the store implements (get/delete)","If write support is genuinely needed, implement set_secret to invoke the cascade with `gix::credentials::helper::NextAction::from(ctx).store(...)` instead of unreachable, mirroring delete_credential","For tests, only exercise the supported surface (get/delete)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Only exercise the supported surface: get/delete via the cascade store\nlet entry = but_secret::entry(&store, service, user)?;\n// supported:\nlet cred = entry.get_credential();\nentry.delete_credential();\n// NOT supported by this store - would panic:\n// entry.set_password(...);","typeGuard":"// keyring::CredentialStore exposes as_any; use it to detect stores lacking set support\nfn supports_set(store: &dyn keyring::CredentialStore) -> bool {\n    store.as_any().downcast_ref::<CascadeStore>().is_none()\n}","tryCatchPattern":null,"preventionTips":["Treat the cascade-backed store as read/delete-only; writes belong to the credential helper prompt flow","Wrap store capability checks behind one helper instead of scattering trait calls","Add tests that only cover get/delete so CI never exercises the unreachable arm"],"tags":["rust","keyring","credentials","panic","unsupported-operation","but-secret"],"backgroundTag":"unsupported-operation-panic","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}