BeyondDimension/SteamTools · error · ApplicationException

TCP port {httpProxyPort} is already occupied by other proces

Error message

TCP port {httpProxyPort} is already occupied by other processes.

What it means

Thrown during Kestrel server startup when the configured HTTP proxy port is already bound by another process. ListenHttpProxy resolves the configured HttpProxyPort and probes it with IsAvailableTcp before calling options.Listen; if the probe fails the listener is never created and the app fails fast rather than hitting an OS bind error later.

Source

Thrown at src/BD.WTTS.Client.Plugins.Accelerator.ReverseProxy/Extensions/KestrelServerOptionsExtensions.cs:32

    {
        options.Limits.MaxRequestBodySize = null;
        options.Limits.MinResponseDataRate = null;
        options.Limits.MinRequestBodyDataRate = null;
    }

    /// <summary>
    /// 监听 Http 代理
    /// </summary>
    /// <param name="options"></param>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void ListenHttpProxy(this KestrelServerOptions options)
    {
        var reverseProxyConfig = options.ApplicationServices.GetRequiredService<IReverseProxyConfig>();
        var httpProxyPort = reverseProxyConfig.HttpProxyPort;

        if (!IReverseProxyConfig.IsAvailableTcp(httpProxyPort))
        {
            throw new ApplicationException(
                $"TCP port {httpProxyPort} is already occupied by other processes.");
        }

        options.Listen(IReverseProxyService.Constants.Instance.ProxyIp, httpProxyPort, listen =>
        {
            listen.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
            var proxyMiddleware = options.ApplicationServices.GetRequiredService<HttpProxyMiddleware>();
            var tunnelMiddleware = options.ApplicationServices.GetRequiredService<TunnelMiddleware>();

            listen.UseFlowAnalyze();
            listen.Use(next => context => proxyMiddleware.InvokeAsync(next, context));
            listen.UseTls();
            listen.Use(next => context => tunnelMiddleware.InvokeAsync(next, context));
        });

        options.GetLogger().LogInformation(
            "Listened http://{ProxyIp}:{httpProxyPort}, HTTP proxy service startup completed.",
            IReverseProxyService.Constants.Instance.ProxyIp, httpProxyPort);

View on GitHub (pinned to c16ffa08e0)

Solutions

  1. Free the port: find and stop the process holding it (e.g. netstat/Get-NetTCPConnection + taskkill) then restart.
  2. Change the configured HttpProxyPort to a free port and restart the service.
  3. Ensure no second instance of the application is running before startup.
  4. If caused by TIME_WAIT, wait briefly or enable SO_REUSEADDR on the conflicting listener.

Example fix

// before: fixed port collision
var httpProxyPort = reverseProxyConfig.HttpProxyPort; // e.g. 8888, already in use

// after: fall back to a free port when the configured one is busy
var httpProxyPort = reverseProxyConfig.HttpProxyPort;
if (!IReverseProxyConfig.IsAvailableTcp(httpProxyPort))
    httpProxyPort = IReverseProxyConfig.GetAvailableTcpPort(httpProxyPort + 1);
Defensive patterns

Strategy: validation

Validate before calling

// Probe the port BEFORE configuring Kestrel and pick an alternative if busy.
int ResolveProxyPort(IReverseProxyConfig cfg)
{
    var port = cfg.HttpProxyPort;
    if (!IReverseProxyConfig.IsAvailableTcp(port))
    {
        Log.Warning($"Configured proxy port {port} busy; searching for a free one.");
        port = IReverseProxyConfig.GetAvailableTcpPort(cfg.HttpProxyPort + 1);
    }
    return port;
}

Try / catch

try { options.ListenHttpProxy(); }
catch (ApplicationException ex) when (ex.Message.Contains("already occupied"))
{
    // Surface to the user with the port number and offer to change it / kill the holder.
    Log.Error(TAG, ex, "Proxy port conflict during startup.");
    throw;
}

Prevention

When it happens

Trigger: Calling ListenHttpProxy (during reverse-proxy startup) when another process — a previous app instance that did not release the socket, a different proxy, or a system service — is already listening on the configured HttpProxyPort.

Common situations: App was killed and the socket is still in TIME_WAIT or held by a zombie process; another accelerator/proxy (Clash, v2ray, Fiddler) occupies the same port; the port was changed in config to one already in use; developer is running two instances.

Related errors


AI-assisted analysis of BeyondDimension/SteamTools@c16ffa08e0 (2026-08-13). Data as JSON: /api/errors/9f67b7dc220e040e. Report an issue: GitHub.