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

  1. Check what closed the metrics listener — look for other code calling Close on prometheusListener or a shutdown path running concurrently.
  2. Verify prometheusListenAddr isn't shared with another component that steals/closes the socket.
  3. If metrics are optional, wrap this cancel so a metrics failure doesn't kill the whole RPC server.
  4. 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

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.