MHSanaei/3x-ui · error

runtime manager not initialised

Error message

runtime manager not initialised

What it means

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.

Source

Thrown at internal/web/service/inbound_node.go:33

	"github.com/mhsanaei/3x-ui/v3/internal/web/runtime"
	"github.com/mhsanaei/3x-ui/v3/internal/xray"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

var reportedRemoteTagConflict sync.Map

// nodeBulkPushThreshold caps how many per-client RPCs a single operation will
// stream to a remote node. Above it, the panel marks the node dirty instead and
// lets one ReconcileNode push converge the whole inbound — far cheaper than M
// sequential round-trips. Small ops stay on the live per-client path.
const nodeBulkPushThreshold = 32

func (s *InboundService) runtimeFor(ib *model.Inbound) (runtime.Runtime, error) {
	mgr := runtime.GetManager()
	if mgr == nil {
		return nil, fmt.Errorf("runtime manager not initialised")
	}
	return mgr.RuntimeFor(ib.NodeID)
}

func (s *InboundService) nodePushPlan(ib *model.Inbound) (runtime.Runtime, bool, bool, error) {
	if ib.NodeID == nil {
		rt, err := s.runtimeFor(ib)
		if err != nil {
			return nil, false, false, nil
		}
		return rt, true, false, nil
	}
	nodeSvc := NodeService{}
	enabled, status, _, _, err := nodeSvc.NodeSyncState(*ib.NodeID)
	if err != nil {
		return nil, false, false, err
	}
	if !enabled || status == "offline" {

View on GitHub (pinned to ad32144c42)

Solutions

  1. 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.
  2. In app code, verify startup order: manager init must precede any service call path (routes/jobs).
  3. If embedding 3x-ui as a library, call the same bootstrap the x-ui command runs before using services.
  4. Add a startup assertion/log so a nil manager fails loudly at boot, not at first request.

Example fix

// before (test):
svc := service.InboundService{}
_, err := svc.SaveInbound(ib) // -> runtime manager not initialised

// after (test):
mgr := runtime.NewManager(...)
runtime.SetManager(mgr)
t.Cleanup(func() { runtime.SetManager(nil) })
_, err := svc.SaveInbound(ib)
Defensive patterns

Strategy: validation

Validate before calling

// Guard before using InboundService in tests or early startup
if runtime.GetManager() == nil {
    return errors.New("runtime manager missing: initialise it before calling inbound services")
}

Type guard

func runtimeReady() bool { return runtime.GetManager() != nil }

Try / catch

if !runtimeReady() {
    if err := bootstrapRuntimeManager(); err != nil { // same init the server does
        return err
    }
}
// now safe to call svc.SaveInbound / DelInboundClientByEmail / ...

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15). Data as JSON: /api/errors/c85aa784659b3fa5. Report an issue: GitHub.