{"record":{"id":"f84bbbff7dd00bf4","repo":"XTLS/Xray-core","slug":"routing-statistics-not-enabled","errorCode":null,"errorMessage":"Routing statistics not enabled.","messagePattern":"Routing statistics not enabled\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/router/command/command.go","lineNumber":109,"sourceCode":"\nfunc (s *routingServer) TestRoute(ctx context.Context, request *TestRouteRequest) (*RoutingContext, error) {\n\tif request.RoutingContext == nil {\n\t\treturn nil, errors.New(\"Invalid routing request.\")\n\t}\n\troute, err := s.router.PickRoute(AsRoutingContext(request.RoutingContext))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif request.PublishResult && s.routingStats != nil {\n\t\tctx, _ := context.WithTimeout(context.Background(), 4*time.Second)\n\t\ts.routingStats.Publish(ctx, route)\n\t}\n\treturn AsProtobufMessage(request.FieldSelectors)(route), nil\n}\n\nfunc (s *routingServer) SubscribeRoutingStats(request *SubscribeRoutingStatsRequest, stream RoutingService_SubscribeRoutingStatsServer) error {\n\tif s.routingStats == nil {\n\t\treturn errors.New(\"Routing statistics not enabled.\")\n\t}\n\tgenMessage := AsProtobufMessage(request.FieldSelectors)\n\tsubscriber, err := stats.SubscribeRunnableChannel(s.routingStats)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer stats.UnsubscribeClosableChannel(s.routingStats, subscriber)\n\tfor {\n\t\tselect {\n\t\tcase value, ok := <-subscriber:\n\t\t\tif !ok {\n\t\t\t\treturn errors.New(\"Upstream closed the subscriber channel.\")\n\t\t\t}\n\t\t\troute, ok := value.(routing.Route)\n\t\t\tif !ok {\n\t\t\t\treturn errors.New(\"Upstream sent malformed statistics.\")\n\t\t\t}\n\t\t\terr := stream.Send(genMessage(route))","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/router/command/command.go#L91-L127","documentation":"SubscribeRoutingStats requires a stats channel for routing events; the server was constructed (or the app booted) without one, so streaming is refused. The channel is only created when routing stats are enabled in the policy/stats configuration path.","triggerScenarios":"Streaming SubscribeRoutingStats when s.routingStats == nil: stats feature not registered or routing statistics not enabled in config.","commonSituations":"Calling SubscribeRoutingStats on an instance whose stats service is absent; policy level stats not enabled; NewRoutingServer called directly with nil routingStats in embedded setups.","solutions":["Enable routing statistics in the instance (add stats + policy configuration) and restart","Ensure the RoutingService is created through the normal app wiring so routingStats is injected","If embedding, pass a non-nil stats.Channel to NewRoutingServer"],"exampleFix":"// before\ncommand.NewRoutingServer(router, nil) // SubscribeRoutingStats -> not enabled\n\n// after\nstatsCh := statsChannelFromApp // obtained via features/stats\ncommand.NewRoutingServer(router, statsCh)","handlingStrategy":"validation","validationCode":"// Check the feature before subscribing:\nif statsFeature == nil || !statsFeature.Running() {\n    return fmt.Errorf(\"routing stats unavailable; enable stats in config\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Enable the stats app and routing stats in config before exposing SubscribeRoutingStats","Feature-detect at client startup and disable the stats UI tab when unavailable","Pass a real stats.Channel when constructing the routing server in embedded builds"],"tags":["routing","stats","grpc","configuration"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}