tokio-rs/tokio · error · io::Error

connect cannot be called on a datagram socket

Error message

connect cannot be called on a datagram socket

What it means

UnixSocket::connect checks ty(); if it's socket2::Type::DGRAM, it returns io::ErrorKind::Other 'connect cannot be called on a datagram socket' before issuing the kernel connect(2). For DGRAM sockets the connectionless connect semantics differ; tokio reserves connect for stream sockets. The doc states this explicitly for new_datagram-created sockets.

Source

Thrown at tokio/src/net/unix/socket.rs:203

        };

        UnixListener::new(mio)
    }

    /// Establishes a Unix connection with a peer at the specified socket address.
    ///
    /// The `UnixSocket` is consumed. Once the connection is established, a
    /// connected [`UnixStream`] is returned. If the connection fails, the
    /// encountered error is returned.
    ///
    /// Calling this function on a socket created by [`new_datagram`] will return an error.
    ///
    /// This calls the `connect(2)` operating-system function.
    ///
    /// [`new_datagram`]: `UnixSocket::new_datagram`
    pub async fn connect(self, path: impl AsRef<Path>) -> io::Result<UnixStream> {
        if self.ty() == socket2::Type::DGRAM {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "connect cannot be called on a datagram socket",
            ));
        }

        let addr = socket2::SockAddr::unix(path)?;
        if let Err(err) = self.inner.connect(&addr) {
            if err.raw_os_error() != Some(libc::EINPROGRESS) {
                return Err(err);
            }
        }
        let mio = {
            use std::os::unix::io::{FromRawFd, IntoRawFd};

            let raw_fd = self.inner.into_raw_fd();
            unsafe { mio::net::UnixStream::from_raw_fd(raw_fd) }
        };

View on GitHub (pinned to 625954f365)

Solutions

  1. Use UnixSocket::new_stream() if you need a connected stream, then call connect().
  2. For datagram exchange, use UnixDatagram::bind / connect (the UnixDatagram API supports a connect-like default-peer) instead of UnixSocket::connect.
  3. Branch on socket type and pick the right client API accordingly.
  4. Add a unit test asserting the socket type matches the call path to catch refactor regressions.

Example fix

// before
let s = UnixSocket::new_datagram()?;
s.connect("/tmp/dgram.sock").await?; // error

// after
let s = UnixSocket::new_stream()?;
let conn = s.connect("/tmp/stream.sock").await?;
Defensive patterns

Strategy: type-guard

Validate before calling

let s = UnixSocket::new_stream()?; // for connect semantics
// If you have an existing fd, check socket2::Type before calling connect.

Type guard

fn is_connect_on_datagram(e: &io::Error) -> bool {
    e.kind() == io::ErrorKind::Other
        && e.to_string() == "connect cannot be called on a datagram socket"
}

Try / catch

match sock.connect(path).await {
    Ok(c) => Ok(c),
    Err(e) if e.to_string() == "connect cannot be called on a datagram socket" => {
        Err(anyhow::anyhow!("use UnixDatagram for datagram sockets"))
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling UnixSocket::connect(path) on a socket built with UnixSocket::new_datagram().

Common situations: Reusing a stream-client template on a datagram socket; protocol role confusion; refactoring that changed socket type without updating the connect call.

Related errors


AI-assisted analysis of tokio-rs/tokio@625954f365 (2026-08-11). Data as JSON: /api/errors/1dbe33dbdf2a24a1. Report an issue: GitHub.