jaegertracing/jaeger · error
error starting cleaner server: %w
Error message
error starting cleaner server: %w
What it means
The storage-cleaner extension starts an internal HTTP server via c.server.ListenAndServe() in a goroutine to expose the purge endpoint. If ListenAndServe fails for any reason other than a clean shutdown (http.ErrServerClosed), the error is wrapped as 'error starting cleaner server' and reported as a fatal component status event to the OTel host, since the goroutine cannot return an error from Start.
Source
Thrown at cmd/jaeger/internal/integration/storagecleaner/extension.go:70
if err := purger.Purge(r.Context()); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("Purge request processed successfully"))
}
mux := http.NewServeMux()
mux.HandleFunc("POST "+URL, purgeHandler)
c.server = &http.Server{
Addr: ":" + c.config.Port,
Handler: mux,
ReadHeaderTimeout: 3 * time.Second,
}
c.telset.Logger.Info("Starting storage cleaner server", zap.String("addr", c.server.Addr))
go func() {
if err := c.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
err = fmt.Errorf("error starting cleaner server: %w", err)
componentstatus.ReportStatus(host, componentstatus.NewFatalErrorEvent(err))
}
}()
return nil
}
func (c *storageCleaner) Shutdown(ctx context.Context) error {
if c.server != nil {
if err := c.server.Shutdown(ctx); err != nil {
return fmt.Errorf("error shutting down cleaner server: %w", err)
}
}
return nil
}
func (*storageCleaner) Dependencies() []component.ID {
return []component.ID{jaegerstorage.ID}View on GitHub (pinned to 806f444784)
Solutions
- Change the storagecleaner extension's server port/address to a free one (check with lsof/netstat for the conflicting listener)
- Stop the other process holding the port or configure it to use a different port
- Verify the configured address is bindable in your environment (0.0.0.0 vs localhost, container networking)
Example fix
// before
extensions:
storagecleaner:
server:
endpoint: localhost:9877
// after (port changed because 9877 was in use)
extensions:
storagecleaner:
server:
endpoint: localhost:9878 Defensive patterns
Strategy: validation
Validate before calling
// before start, check the port is free
conn, err := net.Listen("tcp", "localhost:9877")
if err != nil { /* choose another port */ }
conn.Close() Try / catch
// the extension reports a fatal status event; observe it
otelcol.Run returns the fatal error:
if err := cmd.Execute(); err != nil {
if strings.Contains(err.Error(), "error starting cleaner server") {
log.Fatalf("cleaner port in use: %v", err)
}
} Prevention
- Reserve distinct, documented ports for integration extensions
- Set the cleaner endpoint to 127.0.0.1 with a high ephemeral port
- Check for running jaeger instances before tests
When it happens
Trigger: ListenAndServe returns a non-nil, non-ErrServerClosed error: the configured address/port is already in use, the port lacks permissions, or the listener fails at bind time.
Common situations: Port conflict with another process or a second jaeger instance using the same cleaner port; running in a container without the port published/bound; invalid bind address (e.g. wrong hostname).
Related errors
- cannot start the admin server: %w
- failed to create admin server: %w
- cannot initialize admin server: %w
- dial: %w
- websocket dial %s: %w
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/f994c6e16e5e7f2f.
Report an issue: GitHub.