netbirdio/netbird · error

%s: %w

Error message

%s: %w

What it means

On Windows, listenNamedPipe tried every candidate pipe path (protected name first, then the plain name) and each winio.ListenPipe call failed; errors.Join merges one error per path. The protected name needs administrator/LocalSystem, and failure of both names means the daemon could not serve on either.

Source

Thrown at client/cmd/service_pipe_windows.go:34

// listenNamedPipe creates the daemon control pipe and reports the path it ended
// up on. The security descriptor lets any local caller connect, as a Unix socket
// at 0666 does, and the privileged operations are authorized separately from the
// caller's token.
//
// The protected name comes first so that an unprivileged process cannot take the
// name before the service does. Creating it requires being an administrator or
// LocalSystem, so a daemon an ordinary user runs themselves, as in netstack mode,
// falls back to the plain name; clients try both and check who serves them.
func listenNamedPipe(name string) (net.Listener, string, error) {
	var errs []error
	for _, path := range daemonaddr.PipePaths(name) {
		listener, err := winio.ListenPipe(path, &winio.PipeConfig{
			SecurityDescriptor: ipcauth.DefaultPipeSDDL(),
		})
		if err != nil {
			log.Debugf("not serving the daemon on %s: %v", path, err)
			errs = append(errs, fmt.Errorf("%s: %w", path, err))
			continue
		}
		return listener, path, nil
	}

	return nil, "", errors.Join(errs...)
}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Ensure a single daemon instance: stop the service first (netbird service stop / Stop-Service netbird)
  2. Run the daemon elevated if the protected pipe name is required
  3. Choose a different --daemon-addr pipe name to avoid the collision
  4. Read each joined error: access denied points to privileges, pipe-busy/file-in-use points to an existing listener
Defensive patterns

Strategy: fallback

Validate before calling

for _, p := range daemonaddr.PipePaths(name) {
	if c, err := winio.DialPipe(p, &winio.DialPipeTimeout{Timeout: 100 * time.Millisecond}); err == nil {
		_ = c.Close()
		return fmt.Errorf("pipe %s already served by another daemon", p)
	}
}

Try / catch

listener, path, err := listenNamedPipe(name)
if err != nil {
	// both candidate names failed; the joined errors distinguish privileges from an existing listener
	if isAccessDenied(err) {
		return retryElevated()
	}
	return fallbackToTCPIfAllowed()
}

Prevention

When it happens

Trigger: Another netbird daemon already listening on the pipe names (service running while `service run` or the embedded daemon starts); a security descriptor rejected for the caller's token; a malformed pipe name.

Common situations: Starting a second daemon while the installed Windows service is active; running the netstack/embedded client concurrently with the service.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/257df4c5cf8c1e0f. Report an issue: GitHub.