From d487c66b70254f11c125abd3e29ddbd6f1a9824f Mon Sep 17 00:00:00 2001 From: Felipe Truman Date: Thu, 16 Jul 2026 10:22:44 -0300 Subject: [PATCH] fix(start_proxy): fail fast on bind error instead of false "Started" start() scheduled master.run() via create_task and immediately returned "Started proxy on port N" + set running=True, without waiting for the bind. When the port was already in use, mitmproxy's master.run() died asynchronously with SystemExit(1) ("address already in use"), leaving the controller in a false state: running=True but no proxy listening, plus an unretrieved task exception polluting the event loop. Add a synchronous probe bind before starting the master. If the port is taken, return a clear error and leave running=False. Deterministic and avoids chasing mitmproxy's async SystemExit. Co-Authored-By: Claude Fable 5 --- src/mitmproxy_mcp/core/server.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/mitmproxy_mcp/core/server.py b/src/mitmproxy_mcp/core/server.py index 0a3d2df..fe82777 100644 --- a/src/mitmproxy_mcp/core/server.py +++ b/src/mitmproxy_mcp/core/server.py @@ -101,6 +101,20 @@ async def start( opts.update(save_stream_file=save_path) logger.info("flow_dump_enabled", path=save_path) + # pre-check: fail fast if the port is taken. mitmproxy's master.run() dies + # async with SystemExit on bind failure, which would otherwise leave a false + # "Started" + running=True. A synchronous probe bind is deterministic. + import socket + probe = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + probe.bind((host, port)) + except OSError as e: + probe.close() + self.master = None + logger.error("proxy_start_failed", host=host, port=port, error=str(e)) + return f"Couldn't start the proxy on {host}:{port}: {e}" + probe.close() + self.proxy_task = asyncio.create_task(self.master.run()) self.running = True logger.info("proxy_started", host=host, port=port)