windmill-labs/windmill · error · std::io::Error (AddrNotAvailable)

no pinned address to connect

Error message

no pinned address to connect

What it means

`connect_async_with_proxy` iterates over resolved candidate addresses (e.g. from DNS or a pinned proxy address) attempting a connection each time. This error is the fallback produced when the candidate list was empty or every attempt failed without recording a specific error, so there is no underlying `last_err` to surface. It wraps an `io::Error` with `ErrorKind::AddrNotAvailable` inside the crate's `WsError::Io` variant.

Source

Thrown at backend/windmill-trigger-websocket/src/proxy.rs:113

    // Direct connection. Nothing to pin (IP literal or SSRF guard opted out):
    // preserve the original resolve-and-connect path.
    if pinned_addrs.is_empty() {
        return connect_async(request).await;
    }

    // Pin to a validated address so this connect targets the same IP the SSRF
    // guard checked. Try each in order (e.g. IPv6 then IPv4) until one connects.
    let mut last_err: Option<io::Error> = None;
    for addr in pinned_addrs {
        match TcpStream::connect(addr).await {
            Ok(socket) => {
                return client_async_tls_with_config(request, socket, None, None).await;
            }
            Err(e) => last_err = Some(e),
        }
    }
    Err(WsError::Io(last_err.unwrap_or_else(|| {
        io::Error::new(
            io::ErrorKind::AddrNotAvailable,
            "no pinned address to connect",
        )
    })))
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct ProxyTarget {
    host: String,
    port: u16,
    /// Base64-encoded `user:pass` from URL userinfo, ready to drop into
    /// the `Proxy-Authorization: Basic …` header value.
    basic_auth: Option<String>,
}

/// Resolve the proxy URL string to use for outbound `(scheme, host)`.
///
/// `wss://`/`https://` reads `HTTPS_PROXY`, `ws://`/`http://` reads

View on GitHub (pinned to e474e8803c)

Solutions

  1. Verify the proxy/WebSocket host resolves: run `getent hosts <host>` (or `nslookup`) inside the same network/container as the worker
  2. Check the proxy configuration (env/config file) for a typos, empty host, or wrong scheme in the proxy URL
  3. If behind corporate DNS, ensure the resolver is reachable from the worker container (check /etc/resolv.conf)
  4. Retry after fixing DNS/connectivity; the error is produced at connect time and is transient if DNS was down

Example fix

// before
let proxy = std::env::var("WS_PROXY").ok(); // may be Some("") -> no candidates
// after
let proxy = match std::env::var("WS_PROXY") {
    Ok(v) if !v.trim().is_empty() => Some(v),
    _ => None,
};
Defensive patterns

Strategy: retry

Validate before calling

// resolve the host before connecting
let host = proxy_host_from_config();
let resolved = tokio::net::lookup_host((host.as_str(), 443u16))
    .await
    .expect("proxy host must resolve");
if resolved.into_iter().next().is_none() {
    return Err(anyhow!("proxy host {host} resolved to no addresses"));
}

Try / catch

match connect_async_with_proxy(&url, &proxy).await {
    Err(WsError::Io(e)) if e.kind() == std::io::ErrorKind::AddrNotAvailable => {
        tracing::warn!("no address candidates for proxy, retrying: {e}");
        // exponential backoff / fix DNS config then retry
    }
    Err(e) => return Err(e.into()),
    Ok(_) => {}
}

Prevention

When it happens

Trigger: Calling `connect_async_with_proxy` (via `test_connection` or `get_consumer`) when no address candidates exist to try — e.g. the pinned proxy/socket address list is empty after DNS resolution returns nothing, or all attempts failed before setting `last_err`.

Common situations: Proxy host configured as an empty or unresolvable hostname; DNS outage in a container/K8s cluster; misconfigured `WINDMILL_TRIGGER_WS` proxy env vars; IPv6-only or IPv4-only environments where resolution yields no usable A/AAAA records.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/c2b15f78d3299834. Report an issue: GitHub.