You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Failed to start WebSocket server: Port <PORT NUMBER> is already in use #141
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
after i restart unity the task is disapear, and i can start normally. and this is how it's lookslike on fresh start
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
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.
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.
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:
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.
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.
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.
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.
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
Open the Unity project.
Start MCP Unity with the default port (8090).
Confirm that the MCP client connects successfully and MCP tools are available.
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.
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.
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():
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.
Activity
firdiar commentedon Jun 3, 2026
appearently it's happening even without so many task listed on port
this time the server runs without me running the AI agent, so no connected agent then recompile the script
CoderGamester commentedon Jun 7, 2026
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
firdiar commentedon Jun 10, 2026
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.
firdiar commentedon Jun 13, 2026
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.
CoderGamester commentedon Jun 15, 2026
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
HisenL commentedon Jun 17, 2026
这个bug在unity6中发生的概览比较小,但是在中国版unity(团结引擎)项目中,这是个高频复现的bug。我通常是重启unity来解决这个bug。
CoderGamester commentedon Jul 4, 2026
Fixed on main in c35f184.
What changed:
Validation run:
Please update from main and let me know if the port lock still reproduces after Unity open, script recompile, or play mode transitions.
CoderGamester commentedon Jul 4, 2026
@firdiar @HisenL can you confirm tha thte latest changes fixed the issue for you?
LootGoblin commentedon Jul 21, 2026
Still reproducing on
c35f184— Unity 6, Windows 11.I pinned the package to exactly this commit (
#c35f184d7656bd14e7748f11a05060feeb286d6e), rebuiltServer~, 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 thefinallyblock: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:
The Unity process still holds a real
LISTENINGsocket — not aTIME_WAITremnant — and it persists indefinitely (unchanged over several minutes of observation). Alongside it are accepted client sockets stuck inCLOSE_WAIT, i.e. never closed. That suggests the previousWebSocketServer's listener and its accepted sockets outliveStop(), 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 finerun_tests(EditMode) → domain reload → wedges every timeOnly restarting the editor frees the port. For a project that runs its EditMode suite as a standard gate, that makes
run_testsunusable — 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.
CoderGamester commentedon Jul 24, 2026
Status after MCP Unity 1.4.0:
run_testswas not conclusively fixed or re-verified by those changes.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.
feat(server): detect a bound-but-unserviced port and report it
Merge pull request #156 from TenaciousLibbs/fix/141-detect-unserviced…
1shuqing commentedon Sep 9, 2026
Root cause identified and fixed in #163 (opened against
main).Why the port stays bound:
StopServer()closed clients and the listener inside onetryblock and then cleared the field infinally.CloseAllClients()throws when a client socket is half-closed (CLOSE_WAIT, exactly what a client timeout leaves behind), soWebSocketServer.Stop()was skipped while the reference was still dropped. After the domain unloaded, finalizers no longer run, so the boundTcpListenercould never be closed again — the port kept accepting TCP connections with nothing behind them, and every later bind failed withAddressAlreadyInUse.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.
WhisperofIndigo commentedon Sep 13, 2026
I can reproduce the same issue consistently after a Unity scene reload / assembly reload.
Environment
com.gamelovers.mcp-unity@382a43a30f4d)AutoStartServer: trueAllowRemoteConnections: false::1).Steps to reproduce
8090).Actual result
The MCP client disconnects and does not reconnect.
Unity prints repeated retries and then a final error:
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:
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.
mattis-c commentedon Sep 29, 2026
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.