{"record":{"id":"ad9d0ce6005a9e8c","repo":"block/buzz","slug":"failed-to-install-rustls-crypto-provider","errorCode":null,"errorMessage":"failed to install rustls crypto provider","messagePattern":"failed to install rustls crypto provider","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/buzz-admin/src/main.rs","lineNumber":128,"sourceCode":"#[derive(Subcommand)]\nenum ProductFeedbackCommand {\n    /// List feedback across every community as JSON.\n    List {\n        /// Maximum records to return.\n        #[arg(long, default_value_t = 100, value_parser = clap::value_parser!(u16).range(1..=1000))]\n        limit: u16,\n    },\n}\n\n#[tokio::main]\nasync fn main() {\n    // Install the ring CryptoProvider for rustls. The workspace redis TLS\n    // feature compiles both aws-lc-rs and ring in transitively, so rustls can't\n    // auto-select a provider and would panic on the first rediss:// (ElastiCache)\n    // Redis TLS connection without this. Mirrors buzz-relay's main().\n    rustls::crypto::ring::default_provider()\n        .install_default()\n        .expect(\"failed to install rustls crypto provider\");\n\n    let cli = Cli::parse();\n\n    let code = match run(cli).await {\n        Ok(code) => code,\n        Err(e) => {\n            eprintln!(\"error: {e}\");\n            5\n        }\n    };\n    std::process::exit(code);\n}\n\nasync fn run(cli: Cli) -> Result<i32> {\n    match cli.command {\n        Command::GenerateKey => {\n            let keys = Keys::generate();\n            println!(\"Public key:  {}\", keys.public_key().to_hex());","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-admin/src/main.rs#L110-L146","documentation":"buzz-admin installs the ring CryptoProvider as the process-wide rustls default at the top of main(); the comment explains why — the workspace redis TLS feature compiles both aws-lc-rs and ring, so rustls cannot auto-select and would panic on the first rediss:// connection. install_default() returns Err if any provider is already installed, and the .expect turns that into an immediate panic at process start.","triggerScenarios":"rustls::crypto::ring::default_provider().install_default().expect(...) at crates/buzz-admin/src/main.rs:131-133 panics when another CryptoProvider was already installed in the same process: embedding buzz-admin's run path in a test binary that earlier installed aws-lc-rs, a plugin/dependency calling rustls::crypto::aws_lc_rs::default_provider().install_default() first, or a future double-install added to main() itself.","commonSituations":"Integration tests that initialize rustls for an HTTP mock server before invoking admin code; a dependency upgrading to a rustls version that auto-installs a provider; copy-pasting the provider bootstrap into two crates linked into one binary.","solutions":["Make the install idempotent: only install when CryptoProvider::get_default() is None.","Otherwise ensure exactly one provider install site exists across the whole binary (grep for install_default in the dependency tree).","Keep ring as the single choice — buzz-relay's main() uses the same pattern (per the comment); align any new binary on it.","If a test harness must pre-install, use the same provider (ring) so behavior matches production."],"exampleFix":"// before\nrustls::crypto::ring::default_provider()\n    .install_default()\n    .expect(\"failed to install rustls crypto provider\");\n\n// after\nif rustls::crypto::CryptoProvider::get_default().is_none() {\n    rustls::crypto::ring::default_provider()\n        .install_default()\n        .expect(\"failed to install rustls crypto provider\");\n}","handlingStrategy":"validation","validationCode":"// idempotent bootstrap: install only if no provider is set yet\nif rustls::crypto::CryptoProvider::get_default().is_none() {\n    rustls::crypto::ring::default_provider()\n        .install_default()\n        .expect(\"failed to install rustls crypto provider\");\n}","typeGuard":null,"tryCatchPattern":"// only if you embed admin code in a foreign host that you cannot change:\nlet _ = std::panic::catch_unwind(|| {\n    rustls::crypto::ring::default_provider().install_default()\n}); // ignore double-install; a provider already exists","preventionTips":["Centralize the provider bootstrap in one function used by all binaries (buzz-relay main() mirrors this).","In test binaries, install the provider once in a shared init (ctor or main) before any TLS use.","Standardize on ring across the workspace so aws-lc-rs transitive pulls cannot conflict.","Grep the dependency tree for install_default when adding TLS-related crates."],"tags":["rust","rustls","tls","crypto-provider","panic","startup"],"backgroundTag":"crypto-provider-conflict","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-08-20T04:38:24.874Z","contentChangedAt":"2026-08-20T04:38:24.874Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}