{"record":{"id":"f715bc9cebd2b378","repo":"hatoo/oha","slug":"both-cert-and-key-must-be-specified","errorCode":null,"errorMessage":"Both --cert and --key must be specified","messagePattern":"Both --cert and --key must be specified","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/main.rs","lineNumber":587,"sourceCode":"        }\n        (true, false) => hickory_resolver::config::LookupIpStrategy::Ipv4Only,\n        (false, true) => hickory_resolver::config::LookupIpStrategy::Ipv6Only,\n        (true, true) => hickory_resolver::config::LookupIpStrategy::Ipv4AndIpv6,\n    };\n    let (config, mut resolver_opts) = system_resolv_conf()?;\n    resolver_opts.ip_strategy = ip_strategy;\n    let resolver = hickory_resolver::Resolver::builder_with_config(\n        config,\n        hickory_resolver::net::runtime::TokioRuntimeProvider::default(),\n    )\n    .with_options(resolver_opts)\n    .build()?;\n    let cacert = opts.cacert.as_deref().map(std::fs::read).transpose()?;\n    let client_auth = match (opts.cert, opts.key) {\n        (Some(cert), Some(key)) => Some((std::fs::read(cert)?, std::fs::read(key)?)),\n        (None, None) => None,\n        // Not possible because of clap requires\n        _ => anyhow::bail!(\"Both --cert and --key must be specified\"),\n    };\n\n    let url = url.into_owned();\n    let client = Arc::new(client::Client {\n        request_generator: RequestGenerator {\n            url_generator,\n            https: url.scheme() == \"https\",\n            version: http_version,\n            aws_config,\n            method,\n            headers,\n            body_generator,\n            http_proxy: if opts.proxy.is_some() && url.scheme() == \"http\" {\n                Some(Proxy {\n                    headers: proxy_headers.clone(),\n                    version: proxy_http_version,\n                })\n            } else {","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/hatoo/oha/blob/4efba2d113d165aaaf7533f5d2893e7cc57ebfc1/src/main.rs#L569-L605","documentation":"Mutual TLS client authentication requires both a client certificate (--cert) and its private key (--key). The match on (cert, key) handles Some/Some and None/None; any mixed combination bails. The comment notes clap's requires rules normally make this unreachable, but the code defends defensively.","triggerScenarios":"Programmatic construction of run options bypassing clap validation, providing exactly one of --cert or --key.","commonSituations":"Rarely hit via normal CLI use because clap enforces the pairing; can appear with generated configs or custom option builders that skip clap parsing.","solutions":["Always pass --cert and --key together when using mTLS.","Pass neither if client authentication is not needed.","Verify the option-construction path isn't setting only one field."],"exampleFix":"// before\noha --cert client.pem https://example.com\n// after\noha --cert client.pem --key client-key.pem https://example.com","handlingStrategy":"validation","validationCode":"if opt_cert.is_some() != opt_key.is_some() {\n    return Err(\"--cert and --key must be provided together\".into());\n}","typeGuard":null,"tryCatchPattern":"match run(opts).await {\n    Err(e) if e.to_string().contains(\"Both --cert and --key must be specified\") => {\n        eprintln!(\"mTLS requires both --cert and --key\");\n    }\n    Err(e) => return Err(e),\n    Ok(v) => v,\n}","preventionTips":["Always set --cert and --key as a pair in mTLS configurations.","Rely on clap requires rules rather than building opts programmatically.","Validate option structs before handing them to run()."],"tags":["mtls","cli","tls","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"4efba2d113d165aaaf7533f5d2893e7cc57ebfc1","analyzedAt":"2026-09-09T16:24:23.306Z","contentChangedAt":"2026-09-09T16:24:23.306Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}