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

datagram cannot be called on a stream socket

Error message

datagram cannot be called on a stream socket

What it means

UnixSocket::datagram checks ty(); if it equals socket2::Type::STREAM, it returns io::ErrorKind::Other 'datagram cannot be called on a stream socket' before converting the fd into a UnixDatagram. A stream socket cannot be reinterpreted as a datagram socket, so tokio refuses. The doc notes this fires for new_stream-created sockets.

Source

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

        }
        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) }
        };

        UnixStream::connect_mio(mio).await
    }

    /// Converts the socket into a [`UnixDatagram`].
    ///
    /// Calling this function on a socket created by [`new_stream`] will return an error.
    ///
    /// [`new_stream`]: `UnixSocket::new_stream`
    pub fn datagram(self) -> io::Result<UnixDatagram> {
        if self.ty() == socket2::Type::STREAM {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "datagram cannot be called on a stream socket",
            ));
        }
        let mio = {
            use std::os::unix::io::{FromRawFd, IntoRawFd};

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

        UnixDatagram::from_mio(mio)
    }
}

impl AsRawFd for UnixSocket {
    fn as_raw_fd(&self) -> RawFd {
        self.inner.as_raw_fd()

View on GitHub (pinned to 625954f365)

Solutions

  1. If you need a UnixDatagram, construct with UnixSocket::new_datagram() and then call .datagram().
  2. Keep stream and datagram code paths separate so the conversion matches the construction.
  3. Add a type assertion (check ty() == Type::DGRAM) before calling .datagram() to fail with intent.
  4. Review the abstraction layer that wraps UnixSocket to ensure it routes by socket type.

Example fix

// before
let s = UnixSocket::new_stream()?;
let dg = s.datagram()?; // 'datagram cannot be called on a stream socket'

// after
let s = UnixSocket::new_datagram()?;
s.bind(path)?;
let dg = s.datagram()?;
Defensive patterns

Strategy: type-guard

Validate before calling

let s = UnixSocket::new_datagram()?; // for datagram conversion
// If you have an existing fd, assert socket2::Type::DGRAM before .datagram().

Type guard

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

Try / catch

match sock.datagram() {
    Ok(d) => Ok(d),
    Err(e) if e.to_string() == "datagram cannot be called on a stream socket" => {
        Err(anyhow::anyhow!("use new_datagram() to obtain a UnixDatagram"))
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling UnixSocket::datagram() on a socket created with UnixSocket::new_stream().

Common situations: Constructing a stream socket then trying to repurpose it as datagram; copy-paste across socket kinds; an abstraction that picks the wrong conversion method.

Related errors


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