Skip to content

Failed to start WebSocket server: Port <PORT NUMBER> is already in use #141

Open

Description

@firdiar

Following this closed issue : #28

actually issue still happening on my end sometimes maybe at 50:50 chance

[MCP Unity] Failed to start WebSocket server: Port 8030 is already in use. Only one usage of each socket address (protocol/network address/port) is normally permitted.

The work around i do is usually changing the Port to another number or restarting unity

im checking the task process running at that port when it failed to start and this is the result
Image

after i restart unity the task is disapear, and i can start normally. and this is how it's lookslike on fresh start
Image

i think, somehow the process got stuck while recompile happens forcing the MCP server to restart but the old server is failed to stop completely.

should we have an option to ForceRestart the server?

PS: Let me know if you need any assistance to test it

Activity

  1. firdiar commented on Jun 3, 2026

    @firdiar
    Author

    appearently it's happening even without so many task listed on port

    Image

    this time the server runs without me running the AI agent, so no connected agent then recompile the script

  2. self-assigned this
    on Jun 7, 2026
  3. CoderGamester commented on Jun 7, 2026

    @CoderGamester
    Owner

    Hello @firdiar

    Thank you for the report. I pushed a fix with a delay to confirm the websocket is properly setup.
    I also hadded a Restart button to help in case of race conditions error.

    Let me know if help

  4. firdiar commented on Jun 10, 2026

    @firdiar
    Author

    Just let you know that I'm aware of the update and still testing it.
    So far so good for me. I'll let you know again after several days of using it.

  5. firdiar commented on Jun 13, 2026

    @firdiar
    Author

    After several day using it, thee bug still happening on me at the earlier time i open unity. after i change the port, it's almost never stopped again.

    note: when this bug happen newly restart button still not working on my end.

  6. CoderGamester commented on Jun 15, 2026

    @CoderGamester
    Owner

    Ok, this is very strange.
    Can you give me the step by step execution you do @firdiar ?

    I am wondering now if this is a PC Windows problem

  7. HisenL commented on Jun 17, 2026

    @HisenL

    这个bug在unity6中发生的概览比较小,但是在中国版unity(团结引擎)项目中,这是个高频复现的bug。我通常是重启unity来解决这个bug。

  8. CoderGamester commented on Jul 4, 2026

    @CoderGamester
    Owner

    Fixed on main in c35f184.

    What changed:

    • Unity WebSocket restarts now use a bounded retry backoff instead of only 3 short 100ms attempts, so reload/startup socket cleanup has more time to release the port.
    • Intermediate port-in-use failures now log retry warnings; only the final exhausted retry logs an error.
    • The MCP Unity window now shows pending retry status so it is clear when the server is still recovering.
    • Batch execute tests were adjusted to run correctly in Unity batchmode without requiring the runtime server singleton.

    Validation run:

    • Unity EditMode tests in a temporary Unity 6000.3 project: 59/59 passed.
    • Node Jest tests: 115/115 passed.
    • Node TypeScript build: passed.

    Please update from main and let me know if the port lock still reproduces after Unity open, script recompile, or play mode transitions.

  9. CoderGamester commented on Jul 4, 2026

    @CoderGamester
    Owner

    @firdiar @HisenL can you confirm tha thte latest changes fixed the issue for you?

  10. LootGoblin commented on Jul 21, 2026

    @LootGoblin

    Still reproducing on c35f184 — Unity 6, Windows 11.

    I pinned the package to exactly this commit (#c35f184d7656bd14e7748f11a05060feeb286d6e), rebuilt Server~, and hit it three times in one session. I think the retry-backoff fix can't work here, because the port is never released at all — so no retry budget, however long, will win.

    Evidence

    StopServer() reports full success, including the finally block:

    [MCP Unity] WebSocket client 'Unknown MCP Client' disconnected: Server stopping (Remaining clients: 0)
    [MCP Unity] Closed 1 client connection(s) with code 1000: Server stopping
    [MCP Unity] WebSocket server stopped
    [MCP Unity] WebSocket server stopped and resources cleaned up.
    [MCP Unity] WebSocket server start scheduled in 0.25s (auto-start, attempt 1/10).
    [MCP Unity] Failed to start WebSocket server: Port 8090 is already in use. ...
    [MCP Unity] WebSocket server start scheduled in 0.5s (assembly reload, attempt 2/10).
    [MCP Unity] Failed to start WebSocket server: Port 8090 is already in use. ...
    ... attempts 3/10 .. 10/10, all identical ...
    

    So this is not the if (!IsListening) return; early-return path — the shutdown genuinely runs.

    Socket state afterwards, which I think is the key detail:

    TCP  127.0.0.1:8090  0.0.0.0:0        LISTENING   <unity pid>
    TCP  127.0.0.1:8090  127.0.0.1:xxxxx  CLOSE_WAIT  <unity pid>   (x5, accumulating)
    

    The Unity process still holds a real LISTENING socket — not a TIME_WAIT remnant — and it persists indefinitely (unchanged over several minutes of observation). Alongside it are accepted client sockets stuck in CLOSE_WAIT, i.e. never closed. That suggests the previous WebSocketServer's listener and its accepted sockets outlive Stop(), so the new bind can never succeed.

    There's a second-order effect worth knowing about: because that orphaned listener still accepts TCP, clients connect at the transport layer and then hang until their own connect timeout. From the client side the bridge looks alive but never completes a handshake — which makes this look like a client bug rather than a server one.

    Trigger

    In my case it's specific and completely reproducible:

    • recompile_scripts → domain reload → recovers fine
    • run_tests (EditMode) → domain reload → wedges every time

    Only restarting the editor frees the port. For a project that runs its EditMode suite as a standard gate, that makes run_tests unusable — each call costs a restart.

    Confirming @firdiar's port observation

    Changing the configured port does unwedge it without an editor restart — the new port binds cleanly. It buys one reload before the leak recurs, so it's a good recovery trick but not a fix. That behaviour is consistent with the leak being per-port and permanent for the process lifetime.

    Happy to run any diagnostic that would help — I have a reliable repro.

  11. CoderGamester commented on Jul 24, 2026

    @CoderGamester
    Owner

    Status after MCP Unity 1.4.0:

    • 1.4.0 includes bounded restart backoff, clearer retry state, failed-start cleanup, and additional shutdown/play-mode lifecycle handling.
    • The later report showing a Unity-owned listener remaining bound after run_tests was not conclusively fixed or re-verified by those changes.
    • This therefore remains a valid known limitation of the final 1.x custom WebSocket bridge.

    Keeping this issue open for 1.4.x users. PR #154 removes the custom WebSocket server and port management entirely in 2.0.0 by moving to Unity CLI and Pipeline. Once 2.0.0 ships, this issue can be closed as resolved by the architecture replacement; reports from users pinned to 1.4.0 should remain here.

  12. added 2 commits that reference this issue on Aug 10, 2026
  13. 1shuqing commented on Sep 9, 2026

    @1shuqing

    Root cause identified and fixed in #163 (opened against main).

    Why the port stays bound: StopServer() closed clients and the listener inside one try block and then cleared the field in finally. CloseAllClients() throws when a client socket is half-closed (CLOSE_WAIT, exactly what a client timeout leaves behind), so WebSocketServer.Stop() was skipped while the reference was still dropped. After the domain unloaded, finalizers no longer run, so the bound TcpListener could never be closed again — the port kept accepting TCP connections with nothing behind them, and every later bind failed with AddressAlreadyInUse.

    Verified on Unity 6.3 LTS (6000.3.23f1) / Windows 11: after the fix the server rebinds 1.2 s after a script recompile and 4 s after entering Play Mode; previously both required an editor restart.

  14. WhisperofIndigo commented on Sep 13, 2026

    @WhisperofIndigo

    I can reproduce the same issue consistently after a Unity scene reload / assembly reload.

    Environment

    • OS: Windows 11 Pro, build 26200
    • Unity: 6000.5.10f1
    • MCP Unity: 1.5.0 (com.gamelovers.mcp-unity@382a43a30f4d)
    • Node.js: v24.21.0
    • MCP client: Codex Desktop running natively on Windows (not WSL2)
    • Project path contains spaces, but the package had already started and connected successfully before the failure.
    • AutoStartServer: true
    • AllowRemoteConnections: false
    • The bridge binds to IPv6 loopback (::1).
    • Enter Play Mode Options are enabled with Reload Domain disabled. Play Mode itself now works correctly; this report is about scene reload / assembly reload.

    Steps to reproduce

    1. Open the Unity project.
    2. Start MCP Unity with the default port (8090).
    3. Confirm that the MCP client connects successfully and MCP tools are available.
    4. Make a change that causes Unity to recompile scripts, or accept Unity's Reload Scene prompt.
      • In my case, this happened while an agent was updating UI/UX-related editor scripts.
    5. Wait for Unity to finish the reload.

    Actual result

    The MCP client disconnects and does not reconnect.

    Unity prints repeated retries and then a final error:

    [MCP Unity] Failed to start WebSocket server: Port 8090 is already in use.
    Only one usage of each socket address (protocol/network address/port) is normally permitted.
    
    McpUnity.Unity.McpUnityServer:StartServerInternal
    McpUnity.Unity.McpUnityServer:RunScheduledStart
    McpUnity.Unity.McpUnityServer:StartServerAfterDelayOnUpdate
    

    I observed multiple warning retries followed by the final error, so the built-in retry mechanism is being used but never succeeds.

    Important diagnostic finding

    The port is not owned by another application.

    After the failure:

    • 8090 is still owned by the same main Unity.exe process that opened the project.
    • There is no second Unity Editor instance.
    • A TCP connection to [::1]:8090 can be established, but the listener does not return a WebSocket/HTTP response and eventually times out.
    • This suggests that the previous WebSocketServer listener remains bound but is no longer serving requests after the reload.

    This is different from a normal port conflict or a WSL/networking issue.

    Workaround and additional evidence

    Changing the MCP Unity port from 8090 to 8091 in the Server Window allows the server to start again without closing Unity.

    After doing that, the same main Unity.exe process was listening on both ports simultaneously:
    [::1]:8090 -> Unity.exe
    [::1]:8091 -> Unity.exe

    The old port 8090 remained unresponsive, while the new port 8091 immediately returned:
    HTTP/1.1 401 Unauthorized
    Server: websocket-sharp/1.0
    WWW-Authenticate: Basic realm="MCP Unity"

    That 401 is expected for an unauthenticated liveness request and proves that the new listener is alive. It also strongly suggests that the old listener was leaked rather than properly stopped.
    Restarting the Unity Editor also clears the problem, but changing the port is the only recovery that does not require restarting the editor.

    Expected result

    Before an assembly reload / scene reload, MCP Unity should fully stop and release the previous WebSocket listener. After the reload, the new McpUnityServer instance should bind the configured port and the Node MCP bridge should reconnect automatically.
    The old listener should not remain bound in a non-responsive state.

    Possible direction

    It may be useful to add a forced shutdown/disposal path for the underlying WebSocketServer during beforeAssemblyReload, or detect a listener that accepts TCP connections but no longer responds and recover from it. A “Force Restart / Recover stale listener” action in the Server Window would also be useful.

  15. mattis-c commented on Sep 29, 2026

    @mattis-c

    I ran into this but managed to patch it locally. Here is how Claude explains the problem and the fix we used locally:

    Root cause (Windows): the listening socket is inherited by the Editor's child processes

    I hit this consistently on Windows 11 with Unity 6000.6.0f1 (Mono) and mcp-unity 1.5.0. In my case, entering Play mode stopped the server, and every restart attempt failed with "Port 8090 is already in use" until I restarted the Editor. The Play mode setting made no difference: it happened with domain reload on and off.

    StopServer() wasn't failing. The log showed a clean "WebSocket server stopped" with no errors, and Get-NetTCPConnection -LocalPort 8090 still showed a Listen socket owned by Unity.exe, plus a growing pile of CloseWait connections from the Node bridge retrying.

    What's happening: Mono creates sockets as inheritable on Windows. After the server starts, the Editor keeps launching long-lived child processes (asset import workers, the shader compiler, …), and each one inherits a duplicate handle to the listening socket. WebSocketServer.Stop() closes the Editor's own handle, but the port stays bound as long as any child still holds a
    copy. Windows still reports the Editor as the owning process, which makes it look like the Editor is leaking the socket itself. Restarting the Editor works because the children exit with it. It would also explain why the port is sometimes released after a few minutes on its own: that's when the child holding it exits.

    Repro outside Unity, run with Unity's own Mono (Editor/Data/MonoBleedingEdge/bin/mono.exe) against the websocket-sharp.dll that ships in Editor/Lib:

    for (int i = 1; i <= 2; i++)
    {
    var server = new WebSocketServer("ws://localhost:8097");
    server.AddWebSocketService("/McpUnity");
    server.Start(); // cycle 2 throws: "Only one usage of each socket address..."
    if (i == 1)
    {
    // Any child process started while the listener is open inherits it.
    Process.Start(new ProcessStartInfo("ping", "-n 20 127.0.0.1")
    { UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true });
    }
    server.Stop();
    }

    Without the child process, 15 stop/start cycles succeed, even with a connected client, Basic auth, the liveness probe, and a client reconnecting in a tight loop. With the child, the second Start() fails every time.

    Fix: clear the inherit flag on the listening socket right after Start():

    #if UNITY_EDITOR_WIN
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool SetHandleInformation(IntPtr hObject, uint dwMask, uint dwFlags);

    private static void PreventListenerHandleInheritance(WebSocketServer server)
    {
    // websocket-sharp doesn't expose its TcpListener
    var field = typeof(WebSocketServer).GetField("_listener", BindingFlags.Instance | BindingFlags.NonPublic);
    if (field?.GetValue(server) is TcpListener listener)
    SetHandleInformation(listener.Server.Handle, 0x1 /* HANDLE_FLAG_INHERIT */, 0);
    }
    #endif

    It's called in StartServerInternal() right after webSocketServer.Start(). With this in place the repro passes all cycles with a child process running. In the Editor, the server now comes back about 0.25 s after entering Play mode and after returning to Edit mode, and the tools work during Play mode.

    There's a small window between Start() and SetHandleInformation where a child could still be launched, so this closes the leak in practice rather than in theory.

    About #163: it makes StopServer() close the listener even when CloseAllClients() throws, which is a sensible hardening. But in 1.5.0 CloseAllClients() already catches its own exceptions, and in my case Stop() ran and closed the Editor's handle; the port stayed bound because of the inherited copies. Closing our own handle more forcefully can't release those, so I think both
    changes are needed. Happy to open a PR with this if it's useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions