jaegertracing/jaeger · error
could not start jaeger-query: %w
Error message
could not start jaeger-query: %w
What it means
queryapp.NewServer succeeded but s.server.Start(ctx) failed while binding/starting the HTTP and gRPC listeners. The failure (most commonly a port bind error) is wrapped as 'could not start jaeger-query'.
Source
Thrown at cmd/jaeger/internal/extension/jaegerquery/server.go:148
}
}
s.server, err = queryapp.NewServer(
ctx,
// TODO propagate healthcheck updates up to the collector's runtime
qs,
mqs,
&s.config.QueryOptions,
backendCaps,
tm,
telset,
)
if err != nil {
return fmt.Errorf("could not create jaeger-query: %w", err)
}
if err := s.server.Start(ctx); err != nil {
return fmt.Errorf("could not start jaeger-query: %w", err)
}
// Start the health checker only after the query server is up — a failed
// server start (e.g. port bind error) returns an error from Start, and
// the OTel collector does not call Shutdown in that case. Starting the
// checker first would leak its goroutine forever. The checker is given a
// fresh background context because the Start context is cancelled when
// Start returns; Shutdown stops the checker explicitly.
if s.aiHealth != nil {
s.aiHealth.Start(context.Background()) //nolint:contextcheck // intentional: checker outlives Start ctx; Shutdown stops it.
}
return nil
}
// buildAIHealthChecker constructs an AI health checker when the operator opted in
// (jaeger_query.ai block present with a non-empty agent URL and a positive
// check interval). Returns nil when AI is disabled — there's nothing toView on GitHub (pinned to 806f444784)
Solutions
- Check which process holds the port (ss -ltnp / lsof) and stop it or change the endpoint port
- Change jaeger_query http/grpc endpoint ports in the config
- Ensure the container/pod has the port mapped/free and the process has bind permission
- Check logs for which listener (HTTP vs gRPC) failed to bind
Example fix
// before
jaeger_query:
http:
endpoint: ":16686" # already in use
// after
jaeger_query:
http:
endpoint: ":16687" Defensive patterns
Strategy: try-catch
Validate before calling
// Check port availability before starting
for _, port := range []string{":16686", ":16685"} {
ln, err := net.Listen("tcp", port)
if err != nil {
return fmt.Errorf("port %s in use", port)
}
ln.Close()
} Try / catch
if err := ext.Start(ctx, host); err != nil {
var opErr *net.OpError
if errors.As(err, &opErr) && strings.Contains(opErr.Error(), "address already in use") {
log.Fatalf("query port busy: %v — free the port or change the endpoint", err)
}
return err
} Prevention
- Run one jaeger-query instance per host/pod per port set
- Prefer :0 or explicit unique ports in multi-tenant environments
- Verify no other collector extensions claim the same ports
- Use non-privileged ports or grant bind capabilities
When it happens
Trigger: Another process already holds the configured HTTP (default 16686) or gRPC (16685) port; insufficient privileges for the port; listener creation or TLS setup fails at bind time.
Common situations: Running two jaeger-query instances on one host; port already used by another collector extension; running in a container where the port is occupied by a sidecar; permission denied on privileged ports (<1024).
Related errors
- invalid HTTP server host:port: %w
- invalid gRPC server host:port: %w
- query server failed to initialize listener: %w
- cannot create trace reader: %w
- cannot create dependencies reader: %w
AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01).
Data as JSON: /api/errors/cb49f99d0a1c4759.
Report an issue: GitHub.