vectordotdev/vector · error

systemd fd already consumed

Error message

systemd fd already consumed

What it means

Runtime error from `build_ts_tcp_listener` in src/sources/util/net/tcp/mod.rs. When a socket address is `SocketListenAddr::SystemdFd(offset)`, Vector asks `listenfd` (the systemd socket-activation fds) for the listener at that offset. `take_tcp_listener` returning `None` means no TCP listener exists at that offset — the fd index is out of range or the fd was already taken — and Vector surfaces it as `io::Error` (kind `AddrInUse`, message "systemd fd already consumed").

Source

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

pub async fn try_bind_tcp_listener(
    addr: SocketListenAddr,
    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) => 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 3708c39b12)

Solutions

  1. Run Vector under systemd with the socket unit started so the fds are actually inherited (`systemctl start vector.socket vector.service`)
  2. Align the number and order of `ListenStream=` entries with the configured fd offsets (fd 3 = offset 0, fd 4 = offset 1, ...)
  3. Ensure each configured listener uses a distinct fd — an fd can only be taken once; check reload paths that rebind
  4. When not using socket activation, bind a concrete address (`address = "0.0.0.0:9000"`) instead of `systemd`

Example fix

# before
[sources.in]
type = "socket"
address = "systemd" # fails when run outside systemd

# after (manual/dev run)
[sources.in]
type = "socket"
address = "0.0.0.0:9000"
Defensive patterns

Strategy: validation

Validate before calling

// Before using SocketListenAddr::SystemdFd(offset), 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)
}
// use: systemd_fd_count().is_some_and(|n| n > offset)

Try / catch

match build_tcp_listener(addr, listenfd, tls, reloader).await {
    Err(e) if e.to_string().contains("systemd fd already consumed") => {
        // fd offset out of range or already taken: fix the .socket unit / offsets, don't retry
    }
    other => other,
}

Prevention

When it happens

Trigger: Configuring `address = "systemd"` / `use_systemd_fd = N` while: running Vector outside systemd (no `LISTEN_FDS` passed), the systemd socket unit passing fewer fds than the configured offset (offsets are 0-based over the inherited fds), a second listener re-taking an fd already consumed (e.g. after config reload rebinding), or the systemd unit not actually enabled for socket activation.

Common situations: Debugging a systemd-socket-activated Vector manually (foreground, no systemd) while the config still points at inherited fds; `.socket` units whose `ListenStream=` entries don't line up 1:1 with the Vector sources' fd offsets.

Related errors


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