stalwartlabs/stalwart · error

unwrap_tls called on non-TLS acceptor

Error message

unwrap_tls called on non-TLS acceptor

What it means

unwrap_tls is a convenience method on the result of accepting a connection (TcpAcceptorResult) that extracts the inner TLS acceptor, and it is only valid when the acceptor was actually configured for TLS. Calling it on a Tcp, non-Tls, or failed variant panics with this message. It exists so callers that know TLS is enabled can skip matching every variant.

Source

Thrown at crates/common/src/network/tls.rs:205

                }
            },
            _ => TcpAcceptorResult::Plain(stream),
        }
    }

    pub fn is_tls(&self) -> bool {
        matches!(self, TcpAcceptor::Tls { .. })
    }
}

impl<IO> TcpAcceptorResult<IO>
where
    IO: AsyncRead + AsyncWrite + Unpin,
{
    pub fn unwrap_tls(self) -> Accept<IO> {
        match self {
            TcpAcceptorResult::Tls(accept) => accept,
            _ => panic!("unwrap_tls called on non-TLS acceptor"),
        }
    }
}

impl std::fmt::Debug for CertificateResolver {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.debug_struct("CertificateResolver").finish()
    }
}

View on GitHub (pinned to e962003857)

Solutions

  1. Match on the TcpAcceptorResult variants instead of unwrapping: handle Tls, Tcp, and failed cases explicitly.
  2. If TLS is intended, fix the server configuration so the acceptor is created in TLS mode (valid certificate and key configured).
  3. Guard the call with an is_tls-style check or use the enum's accessor that returns Option instead of panicking.

Example fix

// before
let tls_acceptor = result.unwrap_tls();
// after
let tls_acceptor = match result {
    TcpAcceptorResult::Tls(accept) => accept,
    other => panic!("TLS expected but got non-TLS acceptor: {:?}", other),
};
// or better: handle Tcp gracefully instead of panicking
Defensive patterns

Strategy: type-guard

Type guard

fn is_tls(result: &TcpAcceptorResult<impl AsyncRead + AsyncWrite>) -> bool {
    matches!(result, TcpAcceptorResult::Tls(_))
}

Try / catch

match acceptor_result {
    TcpAcceptorResult::Tls(accept) => { /* TLS path */ }
    TcpAcceptorResult::Tcp(accept) => { /* plaintext fallback */ }
    TcpAcceptorResult::Failed(e) => { /* log accept error */ }
}

Prevention

When it happens

Trigger: Calling `acceptor_result.unwrap_tls()` on a TcpAcceptorResult produced by an acceptor that is not in TLS mode — e.g. the server was configured without a TLS certificate so accepts return Tcp, or the code path calls unwrap_tls unconditionally instead of matching the variant first.

Common situations: A deployment switches TLS off (removes cert/key from config) while application code still assumes TLS and calls unwrap_tls; a code refactor changes acceptor setup so the TLS branch is no longer taken; unit tests constructing a plain TCP acceptor and then calling unwrap_tls.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06). Data as JSON: /api/errors/797502f8cb6132fc. Report an issue: GitHub.