fatedier/frp · info

listener closed

Error message

listener closed

What it means

Returned by frp's vhost Listener.Accept when the internal accept channel has been closed — i.e., the virtual-host listener was closed (Listener.Close closes l.accept, or the owning Muxer shut down). It plays the role of net.ErrClosed for vhost listeners and tells the accept loop to exit.

Source

Thrown at pkg/util/vhost/vhost.go:242

}

type Listener struct {
	name            string
	location        string
	routeByHTTPUser string
	rewriteHost     string
	username        string
	password        string
	mux             *Muxer // for closing Muxer
	accept          chan net.Conn
	ctx             context.Context
}

func (l *Listener) Accept() (net.Conn, error) {
	xl := xlog.FromContextSafe(l.ctx)
	conn, ok := <-l.accept
	if !ok {
		return nil, fmt.Errorf("listener closed")
	}

	// if rewriteHost func is exist
	// rewrite http requests with a modified host header
	// if l.rewriteHost is empty, nothing to do
	if l.mux.rewriteHost != nil {
		sConn, err := l.mux.rewriteHost(conn, l.rewriteHost)
		if err != nil {
			xl.Warnf("host header rewrite failed: %v", err)
			return nil, fmt.Errorf("host header rewrite failed")
		}
		xl.Debugf("rewrite host to [%s] success", l.rewriteHost)
		conn = sConn
	}
	return netpkg.NewContextConn(l.ctx, conn), nil
}

func (l *Listener) Close() error {

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Treat as shutdown: exit the accept loop and release per-conn resources; do not retry Accept
  2. On reload, close listeners only after their accept loops have been signaled to stop (coordinate via context/WaitGroup)
  3. If seen repeatedly with frps still running, check for a proxy name collision or registry error closing the listener early

Example fix

// before
for {
    conn, err := vl.Accept()
    if err != nil { panic(err) }
}

// after
for {
    conn, err := vl.Accept()
    if err != nil { break } // listener closed -> stop serving
    go handle(conn)
}
Defensive patterns

Strategy: try-catch

Try / catch

conn, err := vlistener.Accept()
if err != nil {
    if strings.Contains(err.Error(), "listener closed") {
        return nil // shutdown: propagate as clean stop
    }
    return err
}

Prevention

When it happens

Trigger: Calling Accept() on a vhost Listener after Close(); the muxer closing all listener channels during frps shutdown or proxy unregistration while a ServeHTTP-style accept loop is still blocked in Accept.

Common situations: frps reload/shutdown dropping an http proxy while its handler goroutine is still accepting; code that wraps vhost.Listener in http.Server and closes it on config reload. Seeing this during graceful shutdown is normal.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/7b897457b9841d1d. Report an issue: GitHub.