Mudrit
Guides

Error Handling

Branch on typed error kinds — mudrit_pdfsign::Error and mudrit_keystore::Error — for precise retry UX

Part of whole workspace

Both crates return typed error enums so callers branch on the kind of failure rather than scraping a message string. Both are #[non_exhaustive] — new variants can be added without a breaking change — so every match must include a _ => arm.

Signing errors — mudrit_pdfsign::Error

Categorised by failure kind. Key-source failures preserve the underlying mudrit_keystore::Error.

VariantMeaning
Io(String)File or stream I/O (reading the input, writing the output)
InvalidInput(String)Bad configuration or PDF input — no placements resolved, page selector out of range, SigSize too small, a missing/oversized field
Encryption(String)Encryption / decryption — wrong or absent open password, unsupported cipher, key derivation
Network(String)A network fetch failed — timestamp authority, CRL, or OCSP responder
Signing(String)CMS / PKCS#7 assembly, certificate parsing, or the signature itself
Pdf(String)PDF structure — parsing, building, or serialising the document
Keystore(mudrit_keystore::Error)A key-management failure, preserved with its original type
LtvIncomplete(String)LtvPolicy::Require was set but no CRL / OCSP could be obtained; the message lists the actionable fixes
use mudrit_pdfsign::{sign_pdf, Error};

match sign_pdf(&pdf, &signer, &cfg) {
    Ok(out) => std::fs::write("out.pdf", out)?,
    Err(Error::Network(m))      => eprintln!("retryable — TSA/CRL/OCSP fetch failed: {m}"),
    Err(Error::InvalidInput(m)) => eprintln!("fix the config or input: {m}"),
    Err(Error::Keystore(k))     => handle_key_error(&k),   // inspect the typed key error
    Err(e)                      => eprintln!("signing failed: {e}"),  // _ arm — required
}

Key errors are preserved, not flattened

When a signature fails because of the key source, the original mudrit_keystore::Error (e.g. WrongPin, PinLocked) is carried inside Error::Keystore, and is also reachable via std::error::Error::source. Match on it to drive the same retry UX described below.

Key-management errors — mudrit_keystore::Error

VariantMeaning
CancelledThe user dismissed a certificate-selection dialog
TokenNotFoundNo PKCS#11 token / smart card in any slot
NotFound(String)The requested certificate, alias, or key was not found
WrongPasswordA PFX/P12 password was wrong (or failed its integrity / MAC check)
Authentication(String)Authentication failed — e.g. a wrong token PIN
WrongPin { final_try, count_low }A wrong token PIN; final_try = last attempt before lock, count_low = only a few attempts remain
PinLockedThe token's user PIN is locked — unlock it on the device
KeyUnusable(String)A certificate's private key is missing or unusable
UnsupportedKey(String)The key type / signature scheme is not supported (e.g. a device returned a signature that does not verify as the requested algorithm)
Unsupported(String)The operation is unavailable on this platform (e.g. the Windows store off Windows)
Io { message, source }A filesystem / I/O error; source preserves the original std::io::Error
Backend(String)Any other backend / OS / cryptographic-library error

Retry UX — branch the PIN state

PKCS#11 exposes PIN state as flags, not an exact remaining count, so WrongPin carries final_try and count_low. Branch them to drive an amber / red / locked prompt:

use mudrit_keystore::Error;

fn on_unlock_error(e: &Error) {
    match e {
        Error::WrongPin { final_try: true, .. } => show_red("Wrong PIN — LAST attempt before lock"),
        Error::WrongPin { count_low: true, .. }  => show_amber("Wrong PIN — only a few attempts remain"),
        Error::WrongPin { .. }                    => show_amber("Wrong PIN — try again"),
        Error::PinLocked                          => show_locked("PIN locked — unlock it on the device"),
        Error::WrongPassword                      => show_red("Wrong PFX password"),
        Error::Cancelled                          => { /* user dismissed — no error UI */ }
        _                                         => show_red(&e.to_string()),   // _ arm — required
    }
}

Match with a wildcard arm

Both enums are #[non_exhaustive]. A match without a _ => arm will not compile against a future version of the crate — always include one so new variants degrade gracefully to a generic message.

Next

On this page