unclecode / unclecode/crawl4ai

[Bug]: MCP SSE transport rejects concurrent requests on connect with "Received request before initialization was complete" (-32602)

Open
#2,233 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

⚙️ In-progress 🐞 Bug 📌 Root caused
Dominant language
Python
Stars
83.9k
Forks
8.7k
Avg merge
3d 7h
Merged PRs (30d)
11

Description

crawl4ai version

0.9.2 (Docker server, unclecode/crawl4ai:latest)

Expected Behavior

When an MCP client (such as OpenCode, Claude Code, or multi-agent runtimes) connects over SSE (/mcp/sse) and issues tool calls, inbound requests arriving while initialization is completing should either be briefly buffered/queued or await initialization before processing, similar to how /mcp/ws gates incoming traffic.

Current Behavior

When a client establishes an SSE connection and dispatches tool calls concurrently (e.g. parallel fan-out upon connecting), the requests hit /mcp/messages/?session_id=... before the initialize -> notifications/initialized handshake has completed inside mcp.server.session.ServerSession.

This causes mcp/server/session.py to raise:

WARNING - Failed to validate request: Received request before initialization was complete

The client receives:

{"jsonrpc": "2.0", "id": 1, "error": {"code": -32602, "message": "Invalid request parameters"}}

The error code -32602 misleads clients into reporting that tool call parameters are malformed, when in reality the arguments were never validated because the transport rejected the uninitialized session state.

Context & Contrast with WebSocket

In /app/mcp_bridge.py, the WebSocket handler (/mcp/ws) explicitly mitigates this race condition by synchronizing on initialization:

# /app/mcp_bridge.py (WebSocket handler)
first = adapter.validate_python(await ws.receive_json())
await c2s_send.send(first)
await init_done.wait()          # <-- explicitly waits for server readiness
while True:
    data = await ws.receive_json()
    await c2s_send.send(adapter.validate_python(data))

However, the SSE transport (/mcp/sse + /mcp/messages) directly passes through Starlette to SseServerTransport:

sse = SseServerTransport(f"{base}/messages/")

Without any gating on incoming POST requests during the handshake window, any concurrent requests arriving in the first ~100–500ms get rejected.

Upstream References

This is a known race condition in the underlying Python SDK:

Suggested Fix

A minimal fix in mcp_bridge.py for the SSE transport:

  1. Wrap or intercept incoming POST messages to /mcp/messages to await session initialization (with a small timeout, e.g. 2.0s) before feeding them to the read stream writer, OR
  2. Provide an init_done event per SSE session matching the WebSocket pattern.
Steps to Reproduce
  1. Connect an MCP client over SSE (/mcp/sse).
  2. Immediately upon receiving the endpoint SSE event, dispatch 3+ parallel tools/call requests in under 50ms alongside initialize.
  3. Observe Received request before initialization was complete in Docker logs and -32602 returned to the client.
Environment
  • Docker container unclecode/crawl4ai:latest (0.9.2)
  • Python 3.12 (inside container)
  • Client: OpenCode / Claude Code via SSE transport

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with /app/mcp_bridge.py, comparing the /mcp/ws initialization gate with the SSE /mcp/sse and /mcp/messages flow through SseServerTransport. Reproduce with parallel tools/call requests during the initialize handshake and inspect the resulting session behavior. Done means concurrent SSE requests no longer fail with the pre-initialization -32602 response.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend-api-design
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
66/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.