vaxilu/x-ui · warning
xray is already running
Error message
xray is already running
What it means
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.
Source
Thrown at xray/process.go:145
func (p *process) refreshVersion() {
cmd := exec.Command(GetBinaryPath(), "-version")
data, err := cmd.Output()
if err != nil {
p.version = "Unknown"
} else {
datas := bytes.Split(data, []byte(" "))
if len(datas) <= 1 {
p.version = "Unknown"
} else {
p.version = string(datas[1])
}
}
}
func (p *process) Start() (err error) {
if p.IsRunning() {
return errors.New("xray is already running")
}
defer func() {
if err != nil {
p.exitErr = err
}
}()
data, err := json.MarshalIndent(p.config, "", " ")
if err != nil {
return common.NewErrorf("生成 xray 配置文件失败: %v", err)
}
configPath := GetConfigPath()
err = os.WriteFile(configPath, data, fs.ModePerm)
if err != nil {
return common.NewErrorf("写入配置文件失败: %v", err)
}
View on GitHub (pinned to 9c1be8c57a)
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
Example fix
// before
err := p.Start()
// after
if !p.IsRunning() {
err = p.Start()
} // skip start if already running Defensive patterns
Strategy: validation
Validate before calling
if p.IsRunning() {
// skip Start, already running
return nil
}
return p.Start() Try / catch
if err := p.Start(); err != nil {
if err.Error() == "xray is already running" {
logger.Debug("xray already running; nothing to do")
return nil
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of vaxilu/x-ui@9c1be8c57a (2026-09-02).
Data as JSON: /api/errors/a1ba6c1758525985.
Report an issue: GitHub.