{"record":{"id":"a1ba6c1758525985","repo":"vaxilu/x-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":"xray/process.go","lineNumber":145,"sourceCode":"\nfunc (p *process) refreshVersion() {\n\tcmd := exec.Command(GetBinaryPath(), \"-version\")\n\tdata, err := cmd.Output()\n\tif err != nil {\n\t\tp.version = \"Unknown\"\n\t} else {\n\t\tdatas := bytes.Split(data, []byte(\" \"))\n\t\tif len(datas) <= 1 {\n\t\t\tp.version = \"Unknown\"\n\t\t} else {\n\t\t\tp.version = string(datas[1])\n\t\t}\n\t}\n}\n\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\tp.exitErr = err\n\t\t}\n\t}()\n\n\tdata, err := json.MarshalIndent(p.config, \"\", \"  \")\n\tif err != nil {\n\t\treturn common.NewErrorf(\"生成 xray 配置文件失败: %v\", err)\n\t}\n\tconfigPath := GetConfigPath()\n\terr = os.WriteFile(configPath, data, fs.ModePerm)\n\tif err != nil {\n\t\treturn common.NewErrorf(\"写入配置文件失败: %v\", err)\n\t}\n","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/vaxilu/x-ui/blob/9c1be8c57a53953b47ee7c09a93554e73816f907/xray/process.go#L127-L163","documentation":"process.Start in xray/process.go returns this error when IsRunning() indicates an xray process is already alive. Starting a second instance would conflict on the stats API port and inbound ports, so Start refuses and records the situation via exitErr handling only on real failures. This is an idempotency guard on the lifecycle API.","triggerScenarios":"Calling Start() while a previous xray instance is still running — e.g. calling RestartXray(false) logic paths that start without stopping first, or invoking Start manually after xray was already launched.","commonSituations":"Double-clicking restart in the panel; a script that calls start without checking status; a previous start succeeded but the caller believed it failed and retried; zombie process from an earlier crash keeping the port bound.","solutions":["Check IsRunning() before calling Start and skip if already running","Use RestartXray which stops then starts, instead of raw Start","If a stale process is holding state, stop it (kill the PID) and start again","Investigate why the caller thought xray was stopped — check exitErr and logs"],"exampleFix":"// before\nerr := p.Start()\n// after\nif !p.IsRunning() {\n    err = p.Start()\n} // skip start if already running","handlingStrategy":"validation","validationCode":"if p.IsRunning() {\n    // skip Start, already running\n    return nil\n}\nreturn p.Start()","typeGuard":null,"tryCatchPattern":"if err := p.Start(); err != nil {\n    if err.Error() == \"xray is already running\" {\n        logger.Debug(\"xray already running; nothing to do\")\n        return nil\n    }\n    return err\n}","preventionTips":["Always check IsRunning() before Start","Prefer RestartXray over manual stop/start sequences","Avoid firing start requests concurrently","Track exitErr/logs to understand process state transitions"],"tags":["xray","process-lifecycle","duplicate-start"],"backgroundTag":"process-already-running","analyzedSha":"9c1be8c57a53953b47ee7c09a93554e73816f907","analyzedAt":"2026-09-02T18:46:17.308Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T21:17:11.164Z"}