vectordotdev/vector · error

systemd fd already consumed

Error message

systemd fd already consumed

What it means

Runtime error from `try_bind_udp_socket` in src/sources/util/net/udp.rs. With `SocketListenAddr::SystemdFd(offset)`, the code calls `listenfd.take_udp_socket(offset)`; a `None` result — offset beyond the inherited fds, or the fd already consumed — is returned as `io::Error` (kind `AddrInUse`, message "systemd fd already consumed"). UDP sockets need a matching `ListenDatagram=` systemd unit.

Source

Thrown at src/sources/util/net/udp.rs:17

use std::io;

use listenfd::ListenFd;
use tokio::net::UdpSocket;

use super::SocketListenAddr;

/// Binds a UDP socket to the listen address.
pub async fn try_bind_udp_socket(
    addr: SocketListenAddr,
    mut listenfd: ListenFd,
) -> io::Result<UdpSocket> {
    match addr {
        SocketListenAddr::SocketAddr(addr) => UdpSocket::bind(&addr).await,
        SocketListenAddr::SystemdFd(offset) => match listenfd.take_udp_socket(offset)? {
            Some(socket) => UdpSocket::from_std(socket),
            None => Err(io::Error::new(
                io::ErrorKind::AddrInUse,
                "systemd fd already consumed",
            )),
        },
    }
}

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Declare a `ListenDatagram=` entry in the `.socket` unit for the UDP source and restart it so the fd is inherited
  2. Match the fd offsets in Vector config to the order of systemd's passed fds; use one fd per socket
  3. Outside systemd, bind a concrete address (`address = "0.0.0.0:514"`)

Example fix

# before (unit only had ListenStream=; no datagram fd)
[sources.syslog]
type = "syslog"
address = "systemd"
mode = "udp"

# after
# vector.socket: ListenDatagram=514
[sources.syslog]
type = "syslog"
address = "systemd"
mode = "udp"
Defensive patterns

Strategy: validation

Validate before calling

// Before using SocketListenAddr::SystemdFd for UDP, confirm fds were inherited
fn systemd_fd_count() -> Option<usize> {
    let pid_ok = std::env::var("LISTEN_PID").ok()?.parse::<u32>().ok()? == std::process::id();
    let fds = std::env::var("LISTEN_FDS").ok()?.parse::<usize>().ok()?;
    pid_ok.then_some(fds)
}

Try / catch

match try_bind_udp_socket(addr, listenfd).await {
    Err(e) if e.kind() == std::io::ErrorKind::AddrInUse && e.to_string().contains("systemd") => {
        // no datagram fd at that offset: fix the unit (ListenDatagram=) or bind a real address
    }
    other => other,
}

Prevention

When it happens

Trigger: A UDP source (e.g. `syslog` or `socket` with mode `udp`) configured with `address = "systemd"` while the systemd unit passes only `ListenStream=` (TCP) fds, passes fewer fds than the offset, the process is not systemd-supervised, or the fd was already taken by another bind.

Common situations: Reusing a TCP socket-activation unit for a UDP source (fd exists but is TCP, or is absent); running Vector directly during debugging while the config keeps `systemd` as the bind address.

Related errors


AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20). Data as JSON: /api/errors/8cfc43b9ffad8644. Report an issue: GitHub.