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
- Inspect the Langflow server logs for the 'Internal server error: {e}' line — the detail= message is deliberately generic.
- Confirm the session_id matches a live SSE session created by GET /api/v1/mcp/ on the same server process.
- Reproduce with a minimal JSON-RPC message (initialize, tools/list) to isolate tool bugs from transport bugs.
- 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
- Always correlate client failures with the server log line 'Internal server error: {e}'.
- Make MCP tool implementations return JSON-RPC errors instead of raising.
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
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- Internal server error in Streamable HTTP transport
- HTTP error! status: ${response.status}
- Failed to load file (HTTP ${status})
- Failed to load models. Please check your provider credential
- Failed to reload bundle
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/8c10f585d7308a07.
Report an issue: GitHub.