gastownhall/beads · error

accept: %w

Error message

accept: %w

What it means

acceptLoop exits the whole proxy when listener.Accept() returns a non-shutdown error (anything other than net.ErrClosed or a cancelled context). The error is wrapped as 'accept: %w' so the errgroup fails fast instead of busy-looping on a broken listener. Transient retriable conditions (e.g. EMFILE under load) are deliberately not yet retried.

Source

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

	}
}

func (p *proxyServer) acceptLoop(ctx context.Context) error {
	p.tracef("acceptLoop start (addr=%s)", p.listener.Addr())
	for {
		conn, err := p.listener.Accept()
		if err != nil {
			if errors.Is(err, net.ErrClosed) || ctx.Err() != nil {
				p.tracef("acceptLoop exit (ctx=%v)", ctx.Err())
				return nil
			}
			// Surface non-shutdown accept errors to the errgroup so the
			// proxy fails fast instead of busy-looping. Specific errors that
			// warrant retry (e.g. transient EMFILE under load) can be added
			// here as the need arises.
			p.tracef("acceptLoop error: %v", err)
			p.stats.IncAcceptError()
			return fmt.Errorf("accept: %w", err)
		}
		if tc, ok := conn.(*net.TCPConn); ok {
			_ = tc.SetKeepAlive(true)
			_ = tc.SetKeepAlivePeriod(tcpKeepAlivePeriod)
		}
		p.tracef("acceptLoop accepted (remote=%s)", conn.RemoteAddr())
		p.stats.IncAccept()
		p.conns.Go(func() error {
			return p.handleConn(ctx, conn)
		})
	}
}

func (p *proxyServer) handleConn(ctx context.Context, client net.Conn) error {
	addr := client.RemoteAddr()
	p.tracef("handleConn(%s) start", addr)
	p.activeConns.Add(1)
	defer func() {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Raise the process/file-descriptor limit (ulimit -n 65536 or the container's nofile limit) and restart the proxy.
  2. Check for fd leaks (lsof -p <pid> | wc -l) in the process running the proxy.
  3. Restart the proxy after fixing the condition; it exits by design on this error.
  4. If EMFILE is recurring under load, add a retriable-error branch in acceptLoop (the code comments suggest extending this) instead of failing fast.
  5. Verify the listen address/interface still exists if errors reference address problems.

Example fix

// before: proxy dies on transient EMFILE
return fmt.Errorf("accept: %w", err)
// after (suggested by the code comment): retry transient errors
if errors.Is(err, syscall.EMFILE) || errors.Is(err, syscall.ENFILE) {
    time.Sleep(50 * time.Millisecond)
    continue
}
return fmt.Errorf("accept: %w", err)
Defensive patterns

Strategy: fallback

Validate before calling

var l sysctlFDLimit
if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &l); err == nil && l.Cur < 4096 {
    return fmt.Errorf("raise nofile limit before starting proxy (current %d)", l.Cur)
}

Try / catch

if err := proxy.ListenAndServe(ctx, ...); err != nil {
    if strings.Contains(err.Error(), "accept:") {
        // check ulimit -n / fd leaks, fix, and restart the proxy
        return fmt.Errorf("proxy died on accept: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: The TCP listener hits a fatal accept error: file-descriptor exhaustion (EMFILE/ENFILE), the listen socket becoming invalid, or an OS-level network error on Accept while the proxy is not shutting down.

Common situations: ulimit -n too low while many clients connect; a leak of file descriptors in the same process; container fd limits; sudden interface/address removal causing accept failures.

Related errors


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