langflow-ai/langflow · error · HTTPException

Internal server error: {e}

Error message

Internal server error: {e}

What it means

Catch-all 500 from the MCP SSE POST handler (POST /api/v1/mcp/): any exception other than BrokenResourceError/BrokenPipeError while processing an MCP message escapes sse.handle_post_message. The real cause is only in the server log (logger.aexception with 'Internal server error: {e}'), the response body carries no details.

Source

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

            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
        self._mgr_close: asyncio.Event | None = None

    async def _start_session_manager(self) -> None:

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Inspect the Langflow server logs for the 'Internal server error: {e}' line — the detail= message is deliberately generic.
  2. Confirm the session_id matches a live SSE session created by GET /api/v1/mcp/ on the same server process.
  3. Reproduce with a minimal JSON-RPC message (initialize, tools/list) to isolate tool bugs from transport bugs.
  4. Fix or wrap the failing tool so it returns a JSON-RPC error instead of raising.
Defensive patterns

Strategy: try-catch

Validate before calling

import json

def well_formed_jsonrpc(body: bytes) -> bool:
    try:
        obj = json.loads(body)
        return isinstance(obj, dict) and 'jsonrpc' in obj and 'method' in obj
    except Exception:
        return False

Try / catch

Catch HTTPError with status 500 on the MCP POST; log server-side exception id from logs, do not retry blindly (non-idempotent tool calls).

Prevention

When it happens

Trigger: Malformed JSON-RPC bodies that break the MCP SDK session handler; bugs in a tool implementation invoked during the message; session state corruption when the POST references an unknown/expired session_id; DB errors while resolving the bound user.

Common situations: Custom MCP tools raising unhandled exceptions; sending a POST with a session_id from a previous server restart; SDK version mismatch between client and server MCP protocol versions.

Understand the failure class

Related errors


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