openimsdk/open-im-server · warning
prommetrics %s %w
Error message
prommetrics %s %w
What it means
When the prometheus metrics server goroutine terminates for any reason, it cancels the whole RPC start context with a cause formatted as 'prommetrics <rpcRegisterName> <underlying error>'. This error string is the shutdown reason surfaced through context.Cause — the metrics server exiting is treated as fatal for the process.
Source
Thrown at pkg/common/startrpc/start.go:202
}
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")
} else {
rpcPort, err := datautil.GetElemByIndex(rpcPorts, index)View on GitHub (pinned to 175a7bb067)
Solutions
- Read the wrapped underlying error after 'prommetrics <name>' — it names the actual Serve failure (e.g. 'address already in use').
- Check that prometheusListenAddr is free and not duplicated across RPC services on the same host.
- Determine whether the metrics server exiting should be fatal; if not, log instead of calling cancel.
- Verify graceful shutdown closes the metrics listener deliberately and handles http.ErrServerClosed so normal teardown doesn't produce this error.
Example fix
// before
err := prommetrics.Start(prometheusListener)
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
Validate before calling
ln, err := net.Listen("tcp", prometheusListenAddr)
if err != nil { /* port taken; fail fast before starting RPC */ } Try / catch
if cause := context.Cause(ctx); cause != nil && strings.HasPrefix(cause.Error(), "prommetrics ") {
log.Error("metrics server failure shut down process", "cause", cause)
} Prevention
- Pre-bind the prometheus port at startup to fail fast with a clear error
- Assign unique metrics ports per RPC service on the same host
- Skip the metrics cancel path when prometheus is disabled or optional
When it happens
Trigger: prommetrics.Start(prometheusListener) returns an error (port binding failure at Serve time, listener closed, handler panic) inside the Start goroutine, cancelling the root context.
Common situations: Prometheus port already in use by another process; metrics listener becomes unusable at runtime; startup racing where the listener was closed early; intentional shutdown being misread as a failure.
Related errors
AI-assisted analysis of openimsdk/open-im-server@175a7bb067 (2026-09-04).
Data as JSON: /api/errors/cff007092804cac8.
Report an issue: GitHub.