openimsdk/open-im-server · warning
listener done
Error message
listener done
What it means
The prometheus metrics server goroutine calls prommetrics.Start(listener); Start only returns an error. If it returns nil it means the serve loop ended without an error (listener closed / server returned), which is still fatal for metrics, so the code synthesizes 'listener done' as the error and cancels the root context with 'prommetrics <rpcName> listener done'.
Source
Thrown at pkg/common/startrpc/start.go:200
if err != nil {
return err
}
log.ZDebug(ctx, "prometheus start", "addr", prometheusListener.Addr(), "rpcRegisterName", rpcRegisterName)
target, err := jsonutil.JsonMarshal(prommetrics.BuildDefaultTarget(registerIP, prometheusPort))
if err != nil {
return err
}
if autoSetPorts {
if err = client.SetWithLease(ctx, prommetrics.BuildDiscoveryKey(rpcRegisterName, index), target, prommetrics.TTL); err != nil {
if !errors.Is(err, discovery.ErrNotSupported) {
return err
}
}
}
go func() {
err := prommetrics.Start(prometheusListener)
if err == nil {
err = fmt.Errorf("listener done")
}
cancel(fmt.Errorf("prommetrics %s %w", rpcRegisterName, err))
}()
}
var (
rpcServer *grpc.Server
rpcGracefulStop chan struct{}
)
onGrpcServiceRegistrar := func(desc *grpc.ServiceDesc, impl any) {
if rpcServer != nil {
rpcServer.RegisterService(desc, impl)
return
}
var rpcListenAddr string
if autoSetPorts {
rpcListenAddr = net.JoinHostPort(listenIP, "0")View on GitHub (pinned to 175a7bb067)
Solutions
- Check what closed the metrics listener — look for other code calling Close on prometheusListener or a shutdown path running concurrently.
- Verify prometheusListenAddr isn't shared with another component that steals/closes the socket.
- If metrics are optional, wrap this cancel so a metrics failure doesn't kill the whole RPC server.
- Inspect prommetrics.Start implementation to understand when it returns nil vs error (e.g. http.Serve returning http.ErrServerClosed).
Example fix
// before
err := prommetrics.Start(prometheusListener)
if err == nil {
err = fmt.Errorf("listener done")
}
cancel(fmt.Errorf("prommetrics %s %w", rpcRegisterName, err))
// after
err := prommetrics.Start(prometheusListener)
if errors.Is(err, http.ErrServerClosed) {
return
}
cancel(fmt.Errorf("prommetrics %s %w", rpcRegisterName, err)) Defensive patterns
Strategy: try-catch
Try / catch
if cause := context.Cause(ctx); cause != nil && strings.Contains(cause.Error(), "listener done") {
log.Warn("prometheus metrics server exited silently", "rpc", rpcRegisterName)
} Prevention
- Don't share the metrics listener address with other components
- Treat http.ErrServerClosed as normal teardown, not failure
- Consider making metrics failure non-fatal (log instead of cancel)
When it happens
Trigger: prommetrics.Start returns nil — the prometheus HTTP server stopped serving (listener closed, Serve returned) — or the goroutine otherwise reaches cancel with a nil error converted to 'listener done'.
Common situations: Another component closed the listener; the metrics HTTP handler panicked or was shut down; process teardown cancelling the listener; port listener unexpectedly closed by the OS.
Related errors
AI-assisted analysis of openimsdk/open-im-server@175a7bb067 (2026-09-04).
Data as JSON: /api/errors/14be04cdd0ac83e8.
Report an issue: GitHub.