vi/websocat · error

lint should have caught the missing pkcs12_der option

Error message

lint should have caught the missing pkcs12_der option

What it means

In ssl_accept, the pkcs12_der program option is unwrapped with expect because an earlier lint pass was supposed to reject configurations missing it. If you reach this point, the option is None and the pre-validation did not run or did not cover this code path, so the TLS acceptor cannot be built and the process panics instead of returning a clean error.

Solutions

  1. Provide the pkcs12_der option (the DER-encoded PKCS#12 bundle) along with --ssl in the command line/config
  2. Fix the lint/validation pass so it rejects --ssl without pkcs12_der and reports a user-facing error before accept starts
  3. If options are constructed in code, set progopt.pkcs12_der explicitly before calling ssl_accept
  4. Change the expect into a graceful error return (peer_err) so a missing option yields a diagnostic instead of a panic

Example fix

// before
let der = progopt.pkcs12_der.as_ref()
    .expect("lint should have caught the missing pkcs12_der option");
// after
let der = progopt.pkcs12_der.as_ref().ok_or_else(|| {
    ConfigError::new("--ssl requires the pkcs12_der option")
})?;
Defensive patterns

Strategy: validation

Validate before calling

if progopt.ssl && progopt.pkcs12_der.is_none() {
    return Err("--ssl requires pkcs12_der (PKCS#12 bundle) to be set");
}

Type guard

fn has_tls_bundle(o: &ProgramOptions) -> bool {
    o.pkcs12_der.as_ref().map(|d| !d.is_empty()).unwrap_or(false)
}

Try / catch

// expect() panics cannot be caught by Result handling; validate first or catch_unwind
let tls = std::panic::catch_unwind(|| start_ssl_accept(&progopt));

Prevention

When it happens

Trigger: Starting the program with --ssl (accept mode) without supplying --pkcs12-der, when the CLI lint/option-validation pass is bypassed — e.g. constructing options programmatically, a code path that skips the lint, or a regression that dropped the check.

Common situations: Enabling TLS on a listening socket without a certificate bundle; automation generating config where pkcs12_der is only set in some branches; upgrading and the old flag name no longer populates pkcs12_der.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of vi/websocat@3a3574cd2f (2026-09-12). Data as JSON: /api/errors/a896139342208f31. Report an issue: GitHub.

Appendix: source

Thrown at src/ssl_peer.rs:228

            let (r,w) = tls_stream.split();
            ok(Peer::new(r,w, hup))
        }))
    }
}

pub fn ssl_accept(inner_peer: Peer, _l2r: L2rUser, progopt: Rc<Options>) -> BoxedNewPeerFuture {
    let hup = inner_peer.2;
    let squashed_peer = readwrite::ReadWriteAsync::new(inner_peer.0, inner_peer.1);

    fn gettlsa(cert: &[u8], passwd: &str) -> native_tls::Result<TlsAcceptorExt> {
        let pkcs12 = Pkcs12::from_pkcs12(cert, passwd)?;
        Ok(TlsAcceptorExt::from(TlsAcceptor::builder(pkcs12).build()?))
    }

    let der = progopt
        .pkcs12_der
        .as_ref()
        .expect("lint should have caught the missing pkcs12_der option");
    let passwd = progopt
        .pkcs12_passwd.as_deref()
        .unwrap_or("");
    let tls = match gettlsa(der, passwd) {
        Ok(x) => x,
        Err(e) => return peer_err(e),
    };

    debug!("Accepting a TLS connection");
    Box::new(
        tls.accept(squashed_peer)
            .map_err(box_up_err)
            .and_then(move |tls_stream| {
                info!("Accepted TLS connection");
                match tls_stream.get_ref().peer_certificate() {
                    Ok(Some(_cert)) => {
                        // Does not actually work with native-tls
                        info!("  the client presented an identity certificate.");

View on GitHub (pinned to 3a3574cd2f)