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
- Treat as shutdown: exit the accept loop and release per-conn resources; do not retry Accept
- On reload, close listeners only after their accept loops have been signaled to stop (coordinate via context/WaitGroup)
- 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
- Wrap vhost listeners so their accept loops exit on context cancellation before Close
- On hot reload, sequence: stop accept loop, then Close listener, then re-register
- Don't restart Accept in a retry loop — closure is permanent
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
- channel for udp listener closed
- ssh tunnel server closed
- udp write closed listener
- host header rewrite failed
- control manager is closed
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/7b897457b9841d1d.
Report an issue: GitHub.