From 622beecc4ea9859a6b572777a987d0d66a66a043 Mon Sep 17 00:00:00 2001 From: fraggynikita Date: Thu, 18 Jun 2026 04:30:30 +0300 Subject: [PATCH] Fix explicit null data in server-sent events --- fastapi/routing.py | 2 +- fastapi/sse.py | 3 ++- tests/test_sse.py | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/fastapi/routing.py b/fastapi/routing.py index 48c0c2153..77372ff9b 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -514,7 +514,7 @@ def get_request_handler( # validation (the user may mix types intentionally). if item.raw_data is not None: data_str: str | None = item.raw_data - elif item.data is not None: + elif item.data is not None or "data" in item.model_fields_set: if hasattr(item.data, "model_dump_json"): data_str = item.data.model_dump_json() else: diff --git a/fastapi/sse.py b/fastapi/sse.py index 1e2bd8617..ad963a1c9 100644 --- a/fastapi/sse.py +++ b/fastapi/sse.py @@ -147,7 +147,8 @@ class ServerSentEvent(BaseModel): @model_validator(mode="after") def _check_data_exclusive(self) -> "ServerSentEvent": - if self.data is not None and self.raw_data is not None: + data_is_set = self.data is not None or "data" in self.model_fields_set + if data_is_set and self.raw_data is not None: raise ValueError( "Cannot set both 'data' and 'raw_data' on the same " "ServerSentEvent. Use 'data' for JSON-serialized payloads " diff --git a/tests/test_sse.py b/tests/test_sse.py index 6a9d669fe..c13ff4250 100644 --- a/tests/test_sse.py +++ b/tests/test_sse.py @@ -74,6 +74,11 @@ async def sse_items_string(): yield ServerSentEvent(data="plain text data") +@app.get("/items/stream-null", response_class=EventSourceResponse) +async def sse_items_null(): + yield ServerSentEvent(data=None) + + @app.post("/items/stream-post", response_class=EventSourceResponse) async def sse_items_post() -> AsyncIterable[Item]: for item in items: @@ -216,6 +221,12 @@ def test_string_data_json_encoded(client: TestClient): assert 'data: "plain text data"\n' in response.text +def test_null_data_json_encoded(client: TestClient): + response = client.get("/items/stream-null") + assert response.status_code == 200 + assert response.text == "data: null\n\n" + + def test_server_sent_event_null_id_rejected(): with pytest.raises(ValueError, match="null"): ServerSentEvent(data="test", id="has\0null") @@ -263,6 +274,9 @@ def test_data_and_raw_data_mutually_exclusive(): with pytest.raises(ValueError, match="Cannot set both"): ServerSentEvent(data="json", raw_data="raw") + with pytest.raises(ValueError, match="Cannot set both"): + ServerSentEvent(data=None, raw_data="raw") + def test_sse_on_router_included_in_app(client: TestClient): response = client.get("/api/events")