MHSanaei/3x-ui · error

failed to start panel update job: %w: %s

Error message

failed to start panel update job: %w: %s

What it means

The preferred launch path runs the updater under systemd-run (transient unit). If systemd-run exits non-zero AND its output does not contain the two known 'systemd not available' strings ('System has not been booted with systemd' / 'Failed to connect to bus'), the failure is treated as real: the script file is removed and the wrapped error plus systemd-run's combined output is returned. Known causes: SELinux/AppArmor denial, systemd-run version not supporting the used flags, resource limits on transient units.

Source

Thrown at internal/web/service/panel/panel.go:269

	if systemdRun, err := exec.LookPath("systemd-run"); err == nil {
		unitName := fmt.Sprintf("x-ui-web-update-%d", time.Now().Unix())
		cmd := exec.CommandContext(context.Background(), systemdRun,
			"--unit", unitName,
			"--setenv", "XUI_MAIN_FOLDER="+mainFolder,
			"--setenv", "XUI_SERVICE="+serviceFolder,
			"--setenv", "XUI_UPDATE_TAG="+updateTag,
			"--setenv", runIDEnv,
			"--setenv", statusFileEnv,
			bash, "-lc", updateScript,
		)
		out, err := cmd.CombinedOutput()
		if err != nil {
			output := strings.TrimSpace(string(out))
			if !strings.Contains(output, "System has not been booted with systemd") &&
				!strings.Contains(output, "Failed to connect to bus") {
				_ = os.Remove(scriptPath)
				return 0, fmt.Errorf("failed to start panel update job: %w: %s", err, output)
			}
			logger.Warning("systemd-run is unavailable, falling back to detached update process:", output)
		} else {
			logger.Infof("started panel update job via systemd-run unit %s", unitName)
			launched = true
			return runID, nil
		}
	}

	cmd := exec.CommandContext(context.Background(), bash, "-lc", updateScript)
	cmd.Env = append(os.Environ(),
		"XUI_MAIN_FOLDER="+mainFolder,
		"XUI_SERVICE="+serviceFolder,
		"XUI_UPDATE_TAG="+updateTag,
		runIDEnv,
		statusFileEnv,
	)
	setDetachedProcess(cmd)

View on GitHub (pinned to ad32144c42)

Solutions

  1. Read the appended output in the error message — it is systemd-run's stderr and names the real cause
  2. If it is a policy denial, allow transient units for the panel user or adjust SELinux/AppArmor
  3. As a workaround, run the official update script manually (same script the panel downloads), then restart the panel
Defensive patterns

Strategy: fallback

Validate before calling

if out, err := exec.Command("systemd-run", "--version").CombinedOutput(); err != nil {
    // expect the detached-process fallback; if systemd IS present but this fails, investigate policy
}

Try / catch

_, err := panelService.StartUpdate(false)
if err != nil && strings.Contains(err.Error(), "failed to start panel update job") {
    // parse the appended systemd-run output; if policy denial, fix unit policy or update manually
}

Prevention

When it happens

Trigger: Triggering the update on a host where systemd exists but systemd-run fails for another reason — e.g. 'Failed to start transient service unit: ...' policy denial, or an embedded init with a systemd-run shim that errors oddly.

Common situations: Hardened systems (SELinux enforcing) blocking transient units; old systemd versions (<231) lacking --property/--setenv support variants; OpenRC/Void with a partial systemd compat layer producing unfamiliar errors.

Related errors


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