{"record":{"id":"7ca39656c9343e46","repo":"XTLS/Xray-core","slug":"upstream-closed-the-subscriber-channel","errorCode":null,"errorMessage":"Upstream closed the subscriber channel.","messagePattern":"Upstream closed the subscriber channel\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/router/command/command.go","lineNumber":121,"sourceCode":"\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))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase <-stream.Context().Done():\n\t\t\treturn stream.Context().Err()\n\t\t}\n\t}\n}\n\nfunc (s *routingServer) mustEmbedUnimplementedRoutingServiceServer() {}\n\ntype service struct {","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/app/router/command/command.go#L103-L139","documentation":"During a SubscribeRoutingStats stream, the subscriber channel was closed by the upstream stats channel (typically on shutdown or when the channel is stopped). The stream terminates with this error because no further values can arrive.","triggerScenarios":"xray instance shutting down or the stats channel being closed/stopped while a SubscribeRoutingStats client is still streaming.","commonSituations":"Long-lived subscribers across config reloads or process restarts; observers kept open after the core instance is closed.","solutions":["Treat this as a normal stream-end signal: close the client stream and resubscribe if the instance is still alive","Keep subscriptions tied to the instance lifecycle (cancel before Close)","Retry with backoff in monitoring tooling that must survive restarts"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for {\n    err := subscribeRoutingStats(ctx)\n    if err == nil || ctx.Err() != nil { break }\n    if strings.Contains(err.Error(), \"Upstream closed\") {\n        time.Sleep(backoff.Next()) // instance restarting; resubscribe\n        continue\n    }\n    return err\n}","preventionTips":["Tie subscription lifetime to the core instance; cancel streams before Close","Use reconnect-with-backoff in monitoring clients","Expect this error on graceful shutdown rather than treating it as a bug"],"tags":["routing","stats","streaming","lifecycle"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}