{"record":{"id":"1997cf3b4d0f1d63","repo":"MHSanaei/3x-ui","slug":"xray-is-already-running","errorCode":null,"errorMessage":"xray is already running","messagePattern":"xray is already running","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/xray/process.go","lineNumber":518,"sourceCode":"func (p *process) refreshVersion() {\n\tversion := \"Unknown\"\n\tctx, cancel := context.WithTimeout(context.Background(), xrayVersionTimeout)\n\tdefer cancel()\n\tcmd := exec.CommandContext(ctx, GetBinaryPath(), \"-version\")\n\tif data, err := cmd.Output(); err == nil {\n\t\tif datas := bytes.Split(data, []byte(\" \")); len(datas) > 1 {\n\t\t\tversion = string(datas[1])\n\t\t}\n\t}\n\tp.mu.Lock()\n\tp.version = version\n\tp.mu.Unlock()\n}\n\n// Start launches the Xray process with the current configuration.\nfunc (p *process) Start() (err error) {\n\tif p.IsRunning() {\n\t\treturn errors.New(\"xray is already running\")\n\t}\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlogger.Error(\"Failure in running xray-core process: \", err)\n\t\t\tp.setExitErr(err)\n\t\t}\n\t}()\n\n\tdata, err := json.MarshalIndent(p.config, \"\", \"  \")\n\tif err != nil {\n\t\treturn common.NewErrorf(\"Failed to generate XRAY configuration files: %v\", err)\n\t}\n\n\terr = os.MkdirAll(config.GetLogFolder(), 0o770)\n\tif err != nil {\n\t\tlogger.Warningf(\"Failed to create log folder: %s\", err)\n\t}","sourceCodeStart":500,"sourceCodeEnd":536,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/xray/process.go#L500-L536","documentation":"process.Start() refuses to launch a second xray-core child from the same process object when IsRunning() is already true. The check reads the PID/exit state under the hood, so this fires on double-start races or reusing a process instance that is still alive. It protects against two xray binaries fighting over the same config/API port.","triggerScenarios":"Two concurrent RestartXray calls, or Start called from both the startup task and an API handler; re-Start after a hot-reload attempt that did not go through Stop; racing the crash-restart handler.","commonSituations":"Concurrent admin operations triggering restarts; custom scripts invoking the start path while the panel already started xray; test code that starts the same process object twice.","solutions":["Check IsRunning() before calling Start, or serialize starts behind the existing service-level lock (RestartXray already holds it).","If you truly want a fresh core, call Stop (and wait) before Start — i.e. use RestartXray instead of Start.","Audit for duplicate start paths (startup task + manual API) firing at the same time."],"exampleFix":"// before\nerr := p.Start() // \"xray is already running\"\n\n// after\nif p.IsRunning() {\n    err = p.Stop()\n    if err != nil { return err }\n}\nerr = p.Start()","handlingStrategy":"validation","validationCode":"if p.IsRunning() {\n    // already started; nothing to do or use Restart instead\n    return nil\n}\nreturn p.Start()","typeGuard":null,"tryCatchPattern":"if err := p.Start(); err != nil && strings.Contains(err.Error(), \"already running\") {\n    return nil // idempotent start\n} else if err != nil {\n    return err\n}","preventionTips":["Always start xray through XrayService (RestartXray path) so the package lock serializes starts.","Never call Start concurrently from scripts and panel at boot."],"tags":["xray","lifecycle","concurrency"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}