libnyanpasu/clash-nyanpasu · critical

Can't find an open port

Error message

Can't find an open port

What it means

get_clash_external_port resolves the external controller port. With the AllowFallback strategy, if the configured port is occupied it calls port_scanner::request_open_port() for any free port; if the scanner finds none, this error is thrown. It means no bindable local port could be found at all.

Solutions

  1. Free ports: kill stale listeners or widen the ephemeral range (sysctl net.ipv4.ip_local_port_range).
  2. Check firewall/security software that may block binds on all ports.
  3. Restart the host/network stack to clear TIME_WAIT sockets.
  4. Pin a known-free port or change the strategy so no fallback scan is needed.

Example fix

# before
sysctl net.ipv4.ip_local_port_range  # 32768 60999, exhausted
# after
sudo sysctl -w net.ipv4.ip_local_port_range="1024 65535"
Defensive patterns

Strategy: retry

Validate before calling

// check a bindable port exists before launch (node)
const net = require('net');
const s = net.createServer(); s.listen(0, () => { console.log('bindable'); s.close(); });

Try / catch

try {
  await startCore();
} catch (e) {
  if (String(e).includes("Can't find an open port")) {
    retryWithBackoff(startCore, 3); // transient port exhaustion
  } else throw e;
}

Prevention

When it happens

Trigger: request_open_port() finds every candidate port bound - heavily loaded machines, containers with exhausted ephemeral port ranges, or sandboxes where bind() is denied for all ports.

Common situations: Docker/CI environments with net.ipv4.ip_local_port_range exhausted, huge TIME_WAIT socket counts, or firewall/security software blocking port binds.

Related errors


AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08). Data as JSON: /api/errors/cbad9de78421d7f1. Report an issue: GitHub.

Appendix: source

Thrown at backend/tauri/src/utils/help.rs:174

pub fn get_clash_external_port(
    strategy: &ExternalControllerPortStrategy,
    port: u16,
) -> anyhow::Result<u16> {
    match strategy {
        ExternalControllerPortStrategy::Fixed => {
            if !port_scanner::local_port_available(port) {
                bail!("Port {} is not available", port);
            }
        }
        ExternalControllerPortStrategy::Random | ExternalControllerPortStrategy::AllowFallback => {
            if ExternalControllerPortStrategy::AllowFallback == *strategy
                && port_scanner::local_port_available(port)
            {
                return Ok(port);
            }
            let new_port = port_scanner::request_open_port()
                .ok_or_else(|| anyhow!("Can't find an open port"))?;
            return Ok(new_port);
        }
    }
    Ok(port)
}

pub fn resize_tray_image(img: &[u8], scale_factor: f64) -> Result<Vec<u8>> {
    let img = ImageReader::new(Cursor::new(img))
        .with_guessed_format()?
        .decode()?;
    let width = img.width();
    let height = img.height();
    let src_pixels = img.into_rgba8().into_raw();
    let src_image = ImageRef::new(width, height, &src_pixels, PixelType::U8x4)
        .context("failed to parse image")?;

    // Create container for data of destination image
    let size = (32_f64 * scale_factor).round() as u32; // 32px is the base tray size as the dpi is 96

View on GitHub (pinned to f7dbce2997)