MHSanaei/3x-ui · error

Failed to create test config path: %w

Error message

Failed to create test config path: %w

What it means

createTestConfigPath calls os.CreateTemp(config.GetBinFolderPath(), "xray_test_*.json") to reserve a temp config next to the xray binary. Failure means the bin folder does not exist or is not writable (CreateTemp's documented error conditions: pattern path nonexistent, permission denied, or disk full). Environmental — retrying per-item would not help.

Source

Thrown at internal/web/service/outbound/probe_http.go:288

// runHTTPProbeBatch makes one shared-process attempt for the given items,
// writing per-request outcomes into the items' results. It returns a non-nil
// error only when the process never became usable; retryPerItem reports
// whether splitting the batch into per-item instances could help (true for
// start failures / early exits that a poisoned config would explain, false
// for environmental failures like a missing binary or no free ports).
func runHTTPProbeBatch(items []*httpBatchItem, allOutbounds []any, testURL string, realDelay bool) (retryPerItem bool, err error) {
	ports, release, err := reserveLoopbackPorts(len(items))
	if err != nil {
		return false, fmt.Errorf("Failed to reserve test ports: %w", err)
	}
	defer release()

	cfg := buildBatchTestConfig(items, allOutbounds, ports)

	configPath, err := createTestConfigPath()
	if err != nil {
		return false, fmt.Errorf("Failed to create test config path: %w", err)
	}
	defer os.Remove(configPath)

	proc := newBatchProcess(cfg, configPath)
	defer func() {
		if proc.IsRunning() {
			_ = proc.Stop()
		}
	}()

	// Free the reserved ports just before xray binds them; the window is
	// milliseconds, and a lost race makes xray exit fast, which surfaces
	// below and triggers the per-item retry with fresh ports.
	release()
	if err := proc.Start(); err != nil {
		if errors.Is(err, fs.ErrNotExist) {
			// Binary missing — per-item retries would all fail the same way.
			return false, fmt.Errorf("Failed to start test xray instance: %w", err)

View on GitHub (pinned to ad32144c42)

Solutions

  1. Ensure the bin folder exists and is writable by the panel user: mkdir -p <bin folder> && chown
  2. Check disk space (df -h) and inode usage if the folder is fine
  3. Fix an incorrect XUI_BIN_FOLDER env var

Example fix

# before
XUI_BIN_FOLDER=/opt/xray-bin ./x-ui   # folder absent -> CreateTemp fails

# after
mkdir -p /opt/xray-bin && chown xui /opt/xray-bin
XUI_BIN_FOLDER=/opt/xray-bin ./x-ui
Defensive patterns

Strategy: validation

Validate before calling

if info, err := os.Stat(config.GetBinFolderPath()); err != nil || !info.IsDir() {
    return errors.New("bin folder missing; cannot run outbound test")
}
// optionally probe writability
if f, err := os.CreateTemp(config.GetBinFolderPath(), "pre_*.json"); err != nil {
    return errors.New("bin folder not writable")
} else { f.Close(); os.Remove(f.Name()) }

Try / catch

_, err := runHTTPProbeBatch(items, all, url, true)
if err != nil && strings.Contains(err.Error(), "create test config path") {
    // fix folder perms/disk; do not retry until fixed
}

Prevention

When it happens

Trigger: Running an outbound test when the bin folder (XUI_BIN_FOLDER override or default) is missing, read-only, owned by another user, or the disk/tmpfs is full.

Common situations: XUI_BIN_FOLDER points at a path that does not exist; panel binary run as a user without write access to /usr/local/x-ui/bin; a read-only rootfs container; full disk on a small VPS.

Related errors


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