cloudflare/pingora · error

Tried to listen with no addr specified

Error message

Tried to listen with no addr specified

What it means

The low-level listener builder (ListenerEndpoint::builder() in pingora-core/src/listeners/l4.rs) stores the address in an Option<ServerAddress> set by .listen_addr(addr). The Unix listen() implementation .expect()s it to be Some (l4.rs:338), panicking with 'Tried to listen with no addr specified' when listen() was awaited on a builder whose address was never set.

Source

Thrown at pingora-core/src/listeners/l4.rs:338

        }
    }

    pub fn listen_addr(&mut self, addr: ServerAddress) -> &mut Self {
        self.listen_addr = Some(addr);
        self
    }

    #[cfg(feature = "connection_filter")]
    pub fn connection_filter(&mut self, filter: Arc<dyn ConnectionFilter>) -> &mut Self {
        self.connection_filter = Some(filter);
        self
    }

    #[cfg(unix)]
    pub async fn listen(self, fds: Option<ListenFds>) -> Result<ListenerEndpoint> {
        let listen_addr = self
            .listen_addr
            .expect("Tried to listen with no addr specified");

        let listener = if let Some(fds_table) = fds {
            let addr_str = listen_addr.as_ref();

            // Acquire a per-address async lock so that only one task at a
            // time can go through the check-bind-insert sequence for a given
            // address. The flurry guard is dropped before the await so its
            // !Send pointer does not cross an await point.
            let addr_lock = {
                let guard = BIND_LOCKS.pin();
                match guard.get(addr_str) {
                    Some(existing) => existing.clone(),
                    None => {
                        let new_lock = Arc::new(tokio::sync::Mutex::new(()));
                        match guard.try_insert(addr_str.to_string(), new_lock.clone()) {
                            Ok(inserted) => inserted.clone(),
                            Err(e) => e.current.clone(),
                        }

View on GitHub (pinned to 0046038bd4)

Solutions

  1. Always call .listen_addr(ServerAddress::Tcp("127.0.0.1:6152".into(), None)) on the builder before .listen()
  2. Prefer the higher-level listener/service APIs, which take the address up front and cannot reach listen() addr-less
  3. Set the address at construction time in one helper so no code path can forget it

Example fix

// before
let endpoint = ListenerEndpoint::builder().listen(None).await?; // panic: no addr

// after
let endpoint = ListenerEndpoint::builder()
    .listen_addr(ServerAddress::Tcp("127.0.0.1:6152".into(), None))
    .listen(None)
    .await?;
Defensive patterns

Strategy: validation

Validate before calling

// Single construction point: the builder can never be addr-less
fn tcp_listener(addr: &str) -> ListenerEndpointBuilder {
    ListenerEndpoint::builder()
        .listen_addr(ServerAddress::Tcp(addr.to_string().into(), None))
        .clone()
}

Prevention

When it happens

Trigger: Constructing ListenerEndpoint::builder() (or a wrapper that doesn't set the address) and calling .listen(fds).await without a prior .listen_addr(ServerAddress::Tcp(...)/Uds(...)) call.

Common situations: Custom transport stacks or tests using the low-level l4 listener API directly instead of the higher-level service/listener wrappers (which always set l4, see listeners/mod.rs:180); refactors that moved address setting behind a conditional that silently skipped.

Related errors


AI-assisted analysis of cloudflare/pingora@0046038bd4 (2026-08-16). Data as JSON: /api/errors/eb22e800886a01c1. Report an issue: GitHub.