gastownhall/beads · error

listen on %s: %w

Error message

listen on %s: %w

What it means

This error wraps the failure of net.Listen("tcp", "127.0.0.1:<port>") when the dbproxy tries to open its data listener during ListenAndServe. The library throws it whenever the OS refuses to bind the chosen loopback TCP port, and the underlying OS error is preserved via %w so callers can inspect it with errors.Is.

Source

Thrown at internal/storage/dbproxy/proxy/server.go:227

	defer stopEpochWatch()

	// abortInterruptedStart tears down a doomed start whose stop epoch
	// advanced mid-boot. The interrupting stop is polling proxy.lock under a
	// budget (shutdownConfirmDeadline) far smaller than a backend stop can
	// take, and nothing has been published, so release the lock BEFORE the
	// backend teardown instead of starving the stopper into its timeout.
	abortInterruptedStart := func() error {
		p.stats.IncBackendStop()
		releaseLock()
		_ = stopBackendBounded(p.server)
		return fmt.Errorf("%w for %s: stop epoch advanced during startup", errStartInterrupted, p.rootDir)
	}

	addr := fmt.Sprintf("127.0.0.1:%d", p.port)

	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return fmt.Errorf("listen on %s: %w", addr, err)
	}

	p.listener = ln
	defer func() { _ = ln.Close() }()
	p.stats.IncListenAndServe()
	dataPort, ok := ln.Addr().(*net.TCPAddr)
	if !ok {
		return fmt.Errorf("proxy: unexpected data listener address %T", ln.Addr())
	}

	if _, err := identity.WriteSecret(p.rootDir); err != nil {
		return fmt.Errorf("write proxy secret: %w", err)
	}

	var identMu sync.RWMutex
	identReply := identity.IdentReply{
		Schema:   pidfile.SchemaV2,
		Role:     pidfile.KindProxy,

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check what holds the port with `lsof -i :<port>` or `ss -ltnp` and kill the stale proxy process
  2. Reconfigure p.port to a free port, or confirm the ephemeral/dynamic port selection is enabled
  3. Verify loopback networking is available (container/netns restrictions)
  4. Check firewall/SELinux logs if bind is denied by policy

Example fix

// before
p := proxy.New(proxy.WithPort(8080)) // fixed port, already in use
// after
p := proxy.New(proxy.WithPort(0)) // let the OS pick a free loopback port
Defensive patterns

Strategy: validation

Validate before calling

// Before starting, check the port is bindable
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { log.Fatalf("no loopback networking: %v", err) }
_ = ln.Close()
// or verify the specific port is free:
if c, err := net.DialTimeout("tcp", "127.0.0.1:8080", time.Second); err == nil { c.Close(); log.Fatal("port 8080 in use") }

Try / catch

err := p.ListenAndServe(ctx)
var opErr *net.OpError
if errors.As(err, &opErr) && errors.Is(opErr.Err, syscall.EADDRINUSE) {
    log.Printf("proxy port in use, pick another port")
}

Prevention

When it happens

Trigger: ListenAndServe is called and net.Listen on the proxy's configured port fails - typically because the port is already bound by another process, the port is 0-but-restricted, or the loopback interface is unavailable.

Common situations: A stale proxy process still holds the port (orphaned after a crash); two proxies configured with the same port; running in a sandbox/container without loopback networking; port falls in a blocked ephemeral range; SELinux/AppArmor denying bind.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/f47a689b7f2fcc7c. Report an issue: GitHub.