zeroclaw-labs/zeroclaw · error · anyhow::Error
No credentials found for user '{user_id}'
Error message
No credentials found for user '{user_id}' What it means
remove_credential loads the whole credential store and looks up user_id; when the user has no entry at all, it bails with 'No credentials found' (webauthn.rs:502-504). This is distinct from the sibling error 'Credential ... not found' (webauthn.rs:498-501), which fires when the user exists but that credential_id does not.
Source
Thrown at crates/zeroclaw-runtime/src/security/webauthn.rs:503
}
/// List all credentials for a user.
pub fn list_credentials(&self, user_id: &str) -> Result<Vec<WebAuthnCredential>> {
self.load_credentials_for_user(user_id)
}
/// Remove a credential by ID.
pub fn remove_credential(&self, user_id: &str, credential_id: &str) -> Result<()> {
let mut all = self.load_all_credentials()?;
if let Some(user_creds) = all.get_mut(user_id) {
let before = user_creds.len();
user_creds.retain(|c| c.credential_id != credential_id);
anyhow::ensure!(
user_creds.len() < before,
"Credential '{credential_id}' not found for user '{user_id}'"
);
} else {
anyhow::bail!("No credentials found for user '{user_id}'");
}
self.save_all_credentials(&all)
}
// ── Private helpers ─────────────────────────────────────────
fn generate_challenge(&self) -> Result<String> {
let mut buf = [0u8; CHALLENGE_LEN];
self.rng.fill(&mut buf).map_err(|_| {
::zeroclaw_log::record!(
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
.with_outcome(::zeroclaw_log::EventOutcome::Failure),
"webauthn challenge: RNG fill failed"
);
anyhow::Error::msg("Failed to generate random challenge")
})?;
Ok(URL_SAFE_NO_PAD.encode(buf))View on GitHub (pinned to 88bb9c8533)
Solutions
- Verify the user_id matches exactly what enrollment used — list_credentials(user_id) shows what is stored
- If your delete flow should be idempotent, treat 'no credentials / not found' as success instead of an error
- Check that the credentials store path points to the same store used during registration
Example fix
// before
manager.remove_credential(&user_id, &credential_id)?;
// after: idempotent delete
let creds = manager.list_credentials(&user_id)?;
if creds.iter().any(|c| c.credential_id == credential_id) {
manager.remove_credential(&user_id, &credential_id)?;
}
Ok(()) Defensive patterns
Strategy: validation
Validate before calling
let creds = manager.list_credentials(&user_id)?;
if creds.iter().any(|c| c.credential_id == credential_id) {
manager.remove_credential(&user_id, &credential_id)?;
}
// else: nothing to delete — treat as success Try / catch
If the pre-check is skipped, catch the error and treat both 'No credentials found' and 'not found for user' as idempotent success (or 404 in an admin API), logging the mismatched user_id for audit.
Prevention
- Use one canonical user_id format from enrollment through deletion
- Build delete UIs from list_credentials output so stale entries cannot be submitted
- Make credential deletion idempotent in the API layer
When it happens
Trigger: remove_credential(user_id, credential_id) where the user never enrolled a WebAuthn credential, enrolled under a different user_id (case or prefix mismatch), or the credential store file was reset.
Common situations: Admin UI deletes by email while enrollment stored a UUID; the credentials JSON store was recreated after a wipe; double-submit after the first delete already removed the user's last credential.
Related errors
- createSession failed ({status}): {body}
- matrix: configure either `access_token` or `password`
- Nextcloud Talk: no bot secret configured (set bot_token or w
- Unable to extract public key from attestation object ({} byt
- Unsupported COSE key format (expected uncompressed P-256, go
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/f2d25adfdbc06307.
Report an issue: GitHub.