vectordotdev/vector · error

systemd fd already consumed

Error message

systemd fd already consumed

What it means

When a TCP source is configured with SocketListenAddr::SystemdFd, Vector obtains the listening socket from systemd socket activation (LISTEN_FDS). try_bind_tcp_listener calls listenfd.take_tcp_listener(offset); if systemd already handed out that fd (or no fd was passed), it returns None and Vector raises io::ErrorKind::AddrInUse with 'systemd fd already consumed'. Each fd can only be taken once per process.

Solutions

  1. Ensure each source uses a unique systemd fd offset and that the systemd socket unit passes enough fds.
  2. Run Vector under systemd with the matching .socket unit (Sockets=, ListenStream=) so LISTEN_FDS is set.
  3. If not using socket activation, replace listen_address SystemFd with a plain SocketAddr.
  4. Check for duplicate source configs referencing the same fd.

Example fix

// before
sources.in1.listen_address = "systemd:3"
sources.in2.listen_address = "systemd:3"  // same fd
// after
sources.in1.listen_address = "systemd:0"
sources.in2.listen_address = "0.0.0.0:6000"
Defensive patterns

Strategy: validation

Validate before calling

# Before deploy: verify socket activation
systemctl show vector.socket --property=Listen
# Ensure LISTEN_FDS is set in the Vector unit environment

Prevention

When it happens

Trigger: A source uses SocketListenAddr::SystemdFd(offset) and listenfd.take_tcp_listener(offset) returns None — the fd was already consumed by another component in the same process, or Vector was started without systemd socket activation providing the fd.

Common situations: Two sources configured with the same systemd fd offset; running Vector manually (not under systemd) with SystemdFd listen addresses; systemd unit missing Sockets= or socket activation not enabled; reusing one systemd socket for multiple Vector sources.

Related errors


AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16). Data as JSON: /api/errors/b3305dbc5441aa01. Report an issue: GitHub.

Appendix: source

Thrown at src/sources/util/net/tcp/mod.rs:71

    mut listenfd: ListenFd,
    tls: &MaybeTlsSettings,
    tls_reloader: Option<TlsAcceptorReloader>,
    allowlist: Option<Vec<IpNet>>,
) -> crate::Result<MaybeTlsListener> {
    match addr {
        SocketListenAddr::SocketAddr(addr) => tls
            .bind_reloadable(&addr, tls_reloader)
            .await
            .map_err(Into::into),
        SocketListenAddr::SystemdFd(offset) => match listenfd.take_tcp_listener(offset)? {
            Some(listener) => {
                listener.set_nonblocking(true)?;
                TcpListener::from_std(listener)
                    .map(Into::into)
                    .map_err(Into::into)
            }
            None => {
                Err(io::Error::new(io::ErrorKind::AddrInUse, "systemd fd already consumed").into())
            }
        },
    }
    .map(|listener| listener.with_allowlist(allowlist))
}

#[derive(Clone, Copy, Eq, PartialEq)]
pub enum TcpSourceAck {
    Ack,
    Error,
    Reject,
}

pub trait TcpSourceAcker {
    fn build_ack(self, ack: TcpSourceAck) -> Option<Bytes>;
}

pub struct TcpNullAcker;

View on GitHub (pinned to bdb87aeaa4)