vectordotdev/vector · critical

Failed to bind to listener socket at path: {}. Err: {}

Error message

Failed to bind to listener socket at path: {}. Err: {}

What it means

Unix-stream sources (socket sources configured with a filesystem path) bind a tokio UnixListener at startup and panic if bind() fails, printing the path and the io::Error. Because the bind happens inside the spawned source future, the failure is fatal for the run: the process aborts instead of silently running without the source.

Source

Thrown at src/sources/util/unix_stream.rs:54

/// Passing in different functions for `decoder` and `handle_events` can allow
/// for different source-specific logic (such as decoding syslog messages in the
/// syslog source).
pub fn build_unix_stream_source<D, F, E>(
    listen_path: PathBuf,
    socket_file_mode: Option<u32>,
    decoder: D,
    handle_events: impl Fn(&mut [Event], Option<Bytes>) + Clone + Send + Sync + 'static,
    shutdown: ShutdownSignal,
    out: SourceSender,
) -> crate::Result<Source>
where
    D: tokio_util::codec::Decoder<Item = (F, usize), Error = E> + Clone + Send + 'static,
    E: StreamDecodingError + std::fmt::Display + Send + From<std::io::Error>,
    F: Into<SmallVec<[Event; 1]>> + Send,
{
    Ok(Box::pin(async move {
        let listener = UnixListener::bind(&listen_path).unwrap_or_else(|e| {
            panic!(
                "Failed to bind to listener socket at path: {}. Err: {}",
                listen_path.to_string_lossy(),
                e
            )
        });
        info!(message = "Listening.", path = ?listen_path, r#type = "unix");

        change_socket_permissions(&listen_path, socket_file_mode)
            .expect("Failed to set socket permissions");

        let bytes_received = register!(BytesReceived::from(Protocol::UNIX));

        let connection_open = OpenGauge::new();
        let stream = UnixListenerStream::new(listener).take_until(shutdown.clone());
        tokio::pin!(stream);
        while let Some(socket) = stream.next().await {
            let socket = match socket {
                Err(error) => {

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Remove the stale socket before start: rm -f <path> (systemd ExecStartPre=-/usr/bin/rm -f <path> works well)
  2. Shorten the path and ensure its directory exists and is writable by the Vector user
  3. Ensure exactly one Vector instance uses the path: lsof <path> or ss -x | grep <path>
  4. If a supervisor restarts Vector, add pre-stop cleanup so stop/start cycles don't leak the socket

Example fix

# before (systemd unit)
[Service]
ExecStart=/usr/bin/vector --config /etc/vector/vector.toml

# after
[Service]
ExecStartPre=-/usr/bin/rm -f /var/run/vector/vector.sock
ExecStart=/usr/bin/vector --config /etc/vector/vector.toml
Defensive patterns

Strategy: validation

Validate before calling

# Pre-start check (shell):
if [ -S "$SOCK" ]; then rm -f "$SOCK" || exit 1; fi
mkdir -p "$(dirname "$SOCK")" && [ -w "$(dirname "$SOCK")" ] || exit 1

// Rust (embedding): probe before the source runs
if listen_path.exists() { std::fs::remove_file(&listen_path)?; }
std::fs::create_dir_all(listen_path.parent().unwrap())?;

Prevention

When it happens

Trigger: A stale socket file already exists at the path (previous Vector killed uncleanly); the parent directory does not exist or lacks write permission for the Vector user; the path exceeds the OS unix-socket address limit (~108 chars for sun_path on Linux).

Common situations: Restarting after a crash without cleanup; running under a different user than the socket directory owner; a second Vector instance already bound to the same path; long confined paths in containers and /run vs /var/run symlink confusion.

Related errors


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