{"record":{"id":"c85aa784659b3fa5","repo":"MHSanaei/3x-ui","slug":"runtime-manager-not-initialised","errorCode":null,"errorMessage":"runtime manager not initialised","messagePattern":"runtime manager not initialised","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/web/service/inbound_node.go","lineNumber":33,"sourceCode":"\t\"github.com/mhsanaei/3x-ui/v3/internal/web/runtime\"\n\t\"github.com/mhsanaei/3x-ui/v3/internal/xray\"\n\n\t\"gorm.io/gorm\"\n\t\"gorm.io/gorm/clause\"\n)\n\nvar reportedRemoteTagConflict sync.Map\n\n// nodeBulkPushThreshold caps how many per-client RPCs a single operation will\n// stream to a remote node. Above it, the panel marks the node dirty instead and\n// lets one ReconcileNode push converge the whole inbound — far cheaper than M\n// sequential round-trips. Small ops stay on the live per-client path.\nconst nodeBulkPushThreshold = 32\n\nfunc (s *InboundService) runtimeFor(ib *model.Inbound) (runtime.Runtime, error) {\n\tmgr := runtime.GetManager()\n\tif mgr == nil {\n\t\treturn nil, fmt.Errorf(\"runtime manager not initialised\")\n\t}\n\treturn mgr.RuntimeFor(ib.NodeID)\n}\n\nfunc (s *InboundService) nodePushPlan(ib *model.Inbound) (runtime.Runtime, bool, bool, error) {\n\tif ib.NodeID == nil {\n\t\trt, err := s.runtimeFor(ib)\n\t\tif err != nil {\n\t\t\treturn nil, false, false, nil\n\t\t}\n\t\treturn rt, true, false, nil\n\t}\n\tnodeSvc := NodeService{}\n\tenabled, status, _, _, err := nodeSvc.NodeSyncState(*ib.NodeID)\n\tif err != nil {\n\t\treturn nil, false, false, err\n\t}\n\tif !enabled || status == \"offline\" {","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/service/inbound_node.go#L15-L51","documentation":"Returned by InboundService.runtimeFor when runtime.GetManager() is nil — the process-wide runtime manager was never initialised. Every state-changing inbound/client op is required to dispatch through runtime.Runtime, and without the manager there is no runtime to dispatch to. In the normal server this is set during startup, so seeing it means the service is being used outside a booted panel.","triggerScenarios":"Unit/integration tests that construct InboundService and call inbound CRUD without calling runtime.SetManager (or before web/server bootstrap); code paths invoked during early startup before the manager is installed; a custom embedding that skips panel boot.","commonSituations":"New test helper calls InboundService methods directly; refactoring moved a service call before manager init in main.go/startup; tooling that opens the DB and service layer without booting the web server.","solutions":["In tests, initialise the manager the way the app does (mirror web/server startup: create manager, runtime.SetManager, t.Cleanup to reset) — see existing service tests for the pattern.","In app code, verify startup order: manager init must precede any service call path (routes/jobs).","If embedding 3x-ui as a library, call the same bootstrap the x-ui command runs before using services.","Add a startup assertion/log so a nil manager fails loudly at boot, not at first request."],"exampleFix":"// before (test):\nsvc := service.InboundService{}\n_, err := svc.SaveInbound(ib) // -> runtime manager not initialised\n\n// after (test):\nmgr := runtime.NewManager(...)\nruntime.SetManager(mgr)\nt.Cleanup(func() { runtime.SetManager(nil) })\n_, err := svc.SaveInbound(ib)","handlingStrategy":"validation","validationCode":"// Guard before using InboundService in tests or early startup\nif runtime.GetManager() == nil {\n    return errors.New(\"runtime manager missing: initialise it before calling inbound services\")\n}","typeGuard":"func runtimeReady() bool { return runtime.GetManager() != nil }","tryCatchPattern":"if !runtimeReady() {\n    if err := bootstrapRuntimeManager(); err != nil { // same init the server does\n        return err\n    }\n}\n// now safe to call svc.SaveInbound / DelInboundClientByEmail / ...","preventionTips":["In tests, always install and clean up the runtime manager (SetManager + t.Cleanup) — copy the pattern from existing service tests.","Keep manager initialisation before route registration and cron-job start in startup code.","Fail fast at boot if the manager cannot be created instead of limping into first request."],"tags":["initialization","runtime","testing","bootstrap"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}