stride3d/stride · error · InvalidOperationException

Cannot listen on an unusable interface. Check the IsUsable…

Error message

Cannot listen on an unusable interface. Check the IsUsable property before attemping to bind.

What it means

TcpSocketListener.StartListeningAsync throws InvalidOperationException when asked to bind to an ICommsInterface whose IsUsable property is false. Stride requires callers to verify interface usability (e.g. interface is up, has an address) before binding a listener to it.

Solutions

  1. Check listenOn.IsUsable before calling StartListeningAsync and pick a usable interface
  2. If listenOn is null it binds to IPAddress.Loopback; pass null or a usable interface instead
  3. Re-enumerate NetworkInterface/comms interfaces at listen time instead of caching old ones
  4. Handle the exception by falling back to loopback or another usable interface

Example fix

// before
await listener.StartListeningAsync(port, cachedInterface);
// after
if (cachedInterface.IsUsable)
    await listener.StartListeningAsync(port, cachedInterface);
else
    await listener.StartListeningAsync(port);
Defensive patterns

Strategy: validation

Validate before calling

if (listenOn != null && !listenOn.IsUsable) listenOn = null; // fall back to loopback
await listener.StartListeningAsync(port, listenOn);

Type guard

bool CanBindOn(ICommsInterface i) => i == null || i.IsUsable;

Try / catch

try { await listener.StartListeningAsync(port, iface); }
catch (InvalidOperationException) { await listener.StartListeningAsync(port); }

Prevention

When it happens

Trigger: Calling StartListeningAsync(port, listenOn) with a specific interface instance that is down, disconnected, or otherwise marked not usable.

Common situations: Binding to a WiFi interface that dropped; binding to a VPN/ethernet adapter that went offline; caching an ICommsInterface from startup and using it after the network changed.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/dfc6061d2914c364. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Engine/Engine/Network/Sockets.Implementation.NET/TcpSocketListener.cs:57

        ///     Fired when a new TCP connection has been received.
        ///     Use the <code>SocketClient</code> property of the <code>TcpSocketListenerConnectEventArgs</code>
        ///     to get a <code>TcpSocketClient</code> representing the connection for sending and receiving data.
        /// </summary>
        public EventHandler<TcpSocketListenerConnectEventArgs> ConnectionReceived { get; set; }

        /// <summary>
        ///     Binds the <code>TcpSocketListener</code> to the specified port and listens for TCP connections.
        /// </summary>
        /// <param name="port">The port to listen on.</param>
        /// <param name="listenOn">The <code>CommsInterface</code> to listen on. If unspecified, binds to the loopback interface.</param>
        /// <param name="inheritHandle">Allows handle inheritance. Might be ignored depending on platform.</param>
        /// <returns></returns>
        public Task StartListeningAsync(int port, ICommsInterface listenOn = null, bool inheritHandle = false)
        {
            return Task.Run(() =>
            {
                if (listenOn != null && !listenOn.IsUsable)
                    throw new InvalidOperationException("Cannot listen on an unusable interface. Check the IsUsable property before attemping to bind.");

                var ipAddress = listenOn != null ? ((CommsInterface)listenOn).NativeIpAddress : IPAddress.Loopback;

                _listenCanceller = new CancellationTokenSource();

                _backingTcpListener = new TcpListener(ipAddress, port);
                _backingTcpListener.Start();

                if (Platform.Type == PlatformType.Windows && !inheritHandle)
                    SetHandleInformation(_backingTcpListener.Server.Handle, HANDLE_FLAGS.Inherit, HANDLE_FLAGS.None);

                WaitForConnections(_listenCanceller.Token);
            });
        }

        /// <summary>
        ///     Stops the <code>TcpSocketListener</code> from listening for new TCP connections.
        ///     This does not disconnect existing connections.

View on GitHub (pinned to 96fad776d2)