langflow-ai/langflow · warning · HTTPException

MCP Server disconnected, error: {e}

Error message

MCP Server disconnected, error: {e}

What it means

Raised by the MCP SSE transport POST handler (POST /api/v1/mcp/) when handling the client message raises anyio BrokenResourceError or BrokenPipeError — the SSE connection's underlying socket broke between the GET connect and the POST message. Langflow maps this to HTTP 404 with the disconnect detail and logs it at info level, since a vanished client is usually benign.

Source

Thrown at src/backend/base/langflow/api/v1/mcp.py:237

            except asyncio.CancelledError:
                await logger.ainfo("SSE connection was cancelled")
                raise
            except Exception as e:
                msg = f"Error in MCP: {e!s}"
                await logger.aexception(msg)
                raise
    finally:
        current_user_ctx.reset(token)


@router.post("/", dependencies=[Depends(raise_error_if_astra_cloud_env)])
async def handle_messages(request: Request, current_user: CurrentActiveMCPUser):
    _bind_mcp_transport_user(request, current_user)
    try:
        await sse.handle_post_message(request.scope, request.receive, request._send)  # noqa: SLF001
    except (BrokenResourceError, BrokenPipeError) as e:
        await logger.ainfo("MCP Server disconnected")
        raise HTTPException(status_code=404, detail=f"MCP Server disconnected, error: {e}") from e
    except Exception as e:
        await logger.aerror(f"Internal server error: {e}")
        raise HTTPException(status_code=500, detail=f"Internal server error: {e}") from e


################################################################################
# Streamable HTTP Transport
################################################################################
class StreamableHTTP:
    def __init__(self) -> None:
        self.session_manager: StreamableHTTPSessionManager | None = None
        self._started = False
        self._start_stop_lock = asyncio.Lock()
        # own the lifecycle of the session manager
        # inside an asyncio task to ensure that
        # __aenter__ and __aexit__ happen in the same task
        self._mgr_task: asyncio.Task | None = None
        self._mgr_ready: asyncio.Event | None = None

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Treat as transient: have the MCP client re-establish the SSE session (GET /api/v1/mcp/) and retry the tool call.
  2. Increase proxy idle/read timeouts for /api/v1/mcp/ (e.g. proxy_read_timeout 300s in nginx) so streams are not cut mid-session.
  3. Verify the client sends the session_id query parameter returned by the initial SSE connect on every POST.
  4. If it recurs constantly, check for intermediate load balancers that forbid SSE/buffer responses.

Example fix

# client (python) retry pattern
import httpx
for attempt in range(3):
    try:
        resp = await client.post(f"{base}/api/v1/mcp/?session_id={sid}", json=msg)
        if resp.status_code == 404 and "disconnected" in resp.text:
            sid = await reconnect_sse()  # re-GET /api/v1/mcp/
            continue
        break
    except httpx.TransportError:
        sid = await reconnect_sse()
Defensive patterns

Strategy: retry

Try / catch

try:
    resp = await post_message(sid, msg)
except (httpx.HTTPError,) as e:
    sid = await reconnect_sse(); retry once

Prevention

When it happens

Trigger: MCP client connects to /api/v1/mcp/, the SSE connection drops (client crash, proxy timeout, page close), and the client (or proxy) still delivers or the server attempts to write to the dead channel during handle_post_message; duplicate POSTs after reconnect also hit it.

Common situations: Browser tab closed while an MCP tool call was in flight; nginx/traefik idle timeouts killing SSE streams; MCP client libraries that race reconnect against in-flight POSTs; mobile networks dropping long-lived connections.

Related errors


AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14). Data as JSON: /api/errors/4dd90770e0034959. Report an issue: GitHub.