{"record":{"id":"eb2e559b4e39d2c5","repo":"MHSanaei/3x-ui","slug":"failed-to-reserve-test-ports-w","errorCode":null,"errorMessage":"Failed to reserve test ports: %w","messagePattern":"Failed to reserve test ports: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/web/service/outbound/probe_http.go","lineNumber":280,"sourceCode":"\tfor _, it := range httpItems {\n\t\tif _, ferr := runHTTPProbeBatch([]*httpBatchItem{it}, allOutbounds, testURL, realDelay); ferr != nil {\n\t\t\tit.result.Success = false\n\t\t\tit.result.Error = ferr.Error()\n\t\t}\n\t}\n\treturn results\n}\n\n// runHTTPProbeBatch makes one shared-process attempt for the given items,\n// writing per-request outcomes into the items' results. It returns a non-nil\n// error only when the process never became usable; retryPerItem reports\n// whether splitting the batch into per-item instances could help (true for\n// start failures / early exits that a poisoned config would explain, false\n// for environmental failures like a missing binary or no free ports).\nfunc runHTTPProbeBatch(items []*httpBatchItem, allOutbounds []any, testURL string, realDelay bool) (retryPerItem bool, err error) {\n\tports, release, err := reserveLoopbackPorts(len(items))\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"Failed to reserve test ports: %w\", err)\n\t}\n\tdefer release()\n\n\tcfg := buildBatchTestConfig(items, allOutbounds, ports)\n\n\tconfigPath, err := createTestConfigPath()\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"Failed to create test config path: %w\", err)\n\t}\n\tdefer os.Remove(configPath)\n\n\tproc := newBatchProcess(cfg, configPath)\n\tdefer func() {\n\t\tif proc.IsRunning() {\n\t\t\t_ = proc.Stop()\n\t\t}\n\t}()\n","sourceCodeStart":262,"sourceCodeEnd":298,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/service/outbound/probe_http.go#L262-L298","documentation":"Before spawning the shared xray test process, runHTTPProbeBatch reserves one loopback listener per item via reserveLoopbackPorts (net.Listen on 127.0.0.1:0). Failure means the OS refused new loopback listeners: ephemeral port exhaustion, file-descriptor limit (ulimit -n), or a broken loopback interface. It is classified environmental (retryPerItem=false) — per-item retries would fail identically.","triggerScenarios":"Probing a batch when the process is out of FDs or the ephemeral port range is exhausted (e.g. after heavy churn of xray test processes or other listeners); containers with very low ulimit.","commonSituations":"Docker container with default 1024 FD soft limit while running frequent outbound tests; a leak of sockets/listeners elsewhere; heavy cron probing on a small VPS.","solutions":["Raise the file-descriptor limit (ulimit -n / LimitNOFILE) for the panel process","Reduce the batch size so fewer ports are reserved at once","Check for socket/listener leaks: ss -s, lsof -p <pid> | wc -l","If ephemeral ports are exhausted, widen net.ipv4.ip_local_port_range or wait for TIME_WAIT to drain"],"exampleFix":"# before (container default)\ndocker run ... 3x-ui   # ulimit 1024, batch probe fails\n\n# after\ndocker run --ulimit nofile=65536:65536 ... 3x-ui\n# or in systemd unit:\n[Service]\nLimitNOFILE=65536","handlingStrategy":"fallback","validationCode":"// rough precheck before probing\nif ulimit := runtimeNumFDs(); ulimit > 0 && ulimit < 1024 {\n    // warn: port/FD reservation may fail; raise limit or reduce batch\n}","typeGuard":null,"tryCatchPattern":"results, err := runHTTPProbeBatch(items, all, url, true)\nif err != nil && strings.Contains(err.Error(), \"reserve test ports\") {\n    // environmental: do NOT retry per-item; surface 'raise ulimit / free ports' to user\n}","preventionTips":["Run the panel with LimitNOFILE >= 65536","Keep probe batches small in cron jobs to bound concurrent loopback listeners"],"tags":["network","ports","file-descriptors","outbounds","environment"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}