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 to

View on GitHub (pinned to 806f444784)

Solutions

  1. Check which process holds the port (ss -ltnp / lsof) and stop it or change the endpoint port
  2. Change jaeger_query http/grpc endpoint ports in the config
  3. Ensure the container/pod has the port mapped/free and the process has bind permission
  4. 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

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


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/cb49f99d0a1c4759. Report an issue: GitHub.