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
- Always call .listen_addr(ServerAddress::Tcp("127.0.0.1:6152".into(), None)) on the builder before .listen()
- Prefer the higher-level listener/service APIs, which take the address up front and cannot reach listen() addr-less
- 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
- Always pair ListenerEndpoint::builder() with .listen_addr() in the same expression
- Prefer the higher-level listener/service APIs that take the address as a constructor argument
- In tests, keep one helper that builds listeners so a forgotten address fails one place, not many
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
- Failed to build listeners
- failed to build work-stealing Tokio runtime
- failed to build no-steal Tokio runtime worker
- non-pathname unix sockets not supported as peer
- must have read body buf
AI-assisted analysis of cloudflare/pingora@0046038bd4 (2026-08-16).
Data as JSON: /api/errors/eb22e800886a01c1.
Report an issue: GitHub.