{"record":{"id":"c1aa304535241067","repo":"zeroclaw-labs/zeroclaw","slug":"credential-credential-id-not-found-for-user","errorCode":null,"errorMessage":"Credential '{credential_id}' not found for user '{user_id}'","messagePattern":"Credential '(.+?)' not found for user '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":404,"severity":"warning","filePath":"crates/zeroclaw-runtime/src/security/webauthn.rs","lineNumber":498,"sourceCode":"            cred.sign_count = new_count;\n        }\n        self.save_all_credentials(&all_credentials)?;\n\n        Ok(())\n    }\n\n    /// List all credentials for a user.\n    pub fn list_credentials(&self, user_id: &str) -> Result<Vec<WebAuthnCredential>> {\n        self.load_credentials_for_user(user_id)\n    }\n\n    /// Remove a credential by ID.\n    pub fn remove_credential(&self, user_id: &str, credential_id: &str) -> Result<()> {\n        let mut all = self.load_all_credentials()?;\n        if let Some(user_creds) = all.get_mut(user_id) {\n            let before = user_creds.len();\n            user_creds.retain(|c| c.credential_id != credential_id);\n            anyhow::ensure!(\n                user_creds.len() < before,\n                \"Credential '{credential_id}' not found for user '{user_id}'\"\n            );\n        } else {\n            anyhow::bail!(\"No credentials found for user '{user_id}'\");\n        }\n        self.save_all_credentials(&all)\n    }\n\n    // ── Private helpers ─────────────────────────────────────────\n\n    fn generate_challenge(&self) -> Result<String> {\n        let mut buf = [0u8; CHALLENGE_LEN];\n        self.rng.fill(&mut buf).map_err(|_| {\n            ::zeroclaw_log::record!(\n                ERROR,\n                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                    .with_outcome(::zeroclaw_log::EventOutcome::Failure),","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/security/webauthn.rs#L480-L516","documentation":"remove_credential loads all stored credentials, finds the user's list, and retains everything not matching the given credential_id. If the retain left the list unchanged (the ID was not present), the removal failed and the error names the credential and user. A missing user list raises a separate error.","triggerScenarios":"Deleting a credential that was already removed (double-click, retrying a successful delete); an ID with different encoding or whitespace than stored; a UI list that is stale after another session removed the credential.","commonSituations":"Dashboard delete buttons firing twice; retry-after-timeout resubmits; multiple browser tabs both managing credentials; IDs copied with altered case or padding.","solutions":["Refresh the credential list after any delete and before the next one","Treat delete-not-found as success (idempotent delete) in the calling UI or wrapper","Pass the credential_id exactly as returned by the list/registration API, unmodified"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// idempotent delete: only call remove_credential when present\nlet creds = service.list_credentials(user_id)?;\nif creds.iter().any(|c| c.credential_id == cred_id) {\n    service.remove_credential(user_id, cred_id)?;\n}\nOk(())","typeGuard":null,"tryCatchPattern":"catch the not-found message and return success (idempotent delete semantics) or 404 to let the UI refresh its list — do not surface it as an unexpected error","preventionTips":["Refresh the credential list after every mutation before enabling the next delete action","Disable delete buttons after first click to prevent double submits","Pass credential IDs through unmodified from API responses"],"tags":["webauthn","credential-management","idempotency","rust"],"backgroundTag":"webauthn-credential-not-found","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}