nsqio/nsq · critical

listen (%s) failed - %s

Error message

listen (%s) failed - %s

What it means

nsqadmin.New binds its HTTP listener with net.Listen("tcp", HTTPAddress) (the --http-address flag, default 0.0.0.0:4171). The returned error is wrapped as 'listen (%s) failed' and the process cannot start. Almost always this is the port already being held or the address being unbindable.

Source

Thrown at nsqadmin/nsqadmin.go:120

		}
		n.graphiteURL = url
	}

	if opts.AllowConfigFromCIDR != "" {
		_, _, err := net.ParseCIDR(opts.AllowConfigFromCIDR)
		if err != nil {
			return nil, fmt.Errorf("failed to parse --allow-config-from-cidr (%s) - %s", opts.AllowConfigFromCIDR, err)
		}
	}

	opts.BasePath = normalizeBasePath(opts.BasePath)

	n.logf(LOG_INFO, version.String("nsqadmin"))

	var err error
	n.httpListener, err = net.Listen("tcp", n.getOpts().HTTPAddress)
	if err != nil {
		return nil, fmt.Errorf("listen (%s) failed - %s", n.getOpts().HTTPAddress, err)
	}

	return n, nil
}

func normalizeBasePath(p string) string {
	if len(p) == 0 {
		return "/"
	}
	// add leading slash
	if p[0] != '/' {
		p = "/" + p
	}
	return path.Clean(p)
}

func (n *NSQAdmin) getOpts() *Options {
	return n.opts.Load().(*Options)

View on GitHub (pinned to 85cf10c09c)

Solutions

  1. Find and stop the holder: lsof -i :4171 or ss -ltnp | grep 4171, then restart nsqadmin
  2. Pick a different port via --http-address=0.0.0.0:4172 (or :4172 for all interfaces)
  3. Use a fully qualified host:port (127.0.0.1:4171) and bracket IPv6 literals ([::1]:4171)
  4. For ports <1024, run under a capability-granting unit (AmbientCapabilities=CAP_NET_BIND_SERVICE) instead of root

Example fix

# before
--http-address=0.0.0.0:4171   # port already in use by another nsqadmin

# after
--http-address=0.0.0.0:4172
Defensive patterns

Strategy: try-catch

Validate before calling

// probe before full startup
ln, err := net.Listen("tcp", opts.HTTPAddress)
if err != nil {
	log.Fatalf("address %s not bindable: %v", opts.HTTPAddress, err)
}
ln.Close()

Type guard

func isAddrInUse(err error) bool {
	if errors.Is(err, syscall.EADDRINUSE) {
		return true
	}
	var opErr *net.OpError
	return errors.As(err, &opErr) && errors.Is(opErr.Err, syscall.EADDRINUSE)
}

Try / catch

n, err := nsqadmin.New(opts)
if err != nil {
	if isAddrInUse(err) {
		// port held: free it or pick another port, then restart (do not loop tightly)
	}
	log.Fatal(err)
}

Prevention

When it happens

Trigger: Another nsqadmin (or any process) already bound 4171; a privileged port (<1024) chosen while running as non-root; malformed --http-address (missing port, bad host literal, e.g. 'localhost' without ':4171'); IPv6 literal not in brackets; firewall/SELinux denying the bind.

Common situations: Running two nsqadmin instances with the same flags; a container without distinct port mappings; systemd restart racing a socket held by a dying process; switching from 0.0.0.0:4171 to a specific interface IP that no longer exists on the host.

Related errors


AI-assisted analysis of nsqio/nsq@85cf10c09c (2026-08-16). Data as JSON: /api/errors/da67823891081fb4. Report an issue: GitHub.