tiangolo/fastapi · error · ValueError
SSE '{field_name}' must be a single line
Error message
SSE '{field_name}' must be a single line What it means
`ServerSentEvent` applies `AfterValidator(_check_single_line)` (sse.py:36-39, 97-122) to the `event` and `id` fields. If the value contains `\r` or `\n`, pydantic raises `ValueError`, which surfaces as a validation error when the event is constructed. SSE wire format encodes each field on a single line; embedded line breaks would corrupt the stream framing.
Source
Thrown at fastapi/sse.py:38
class EventSourceResponse(StreamingResponse):
"""Streaming response with `text/event-stream` media type.
Use as `response_class=EventSourceResponse` on a *path operation* that uses `yield`
to enable Server Sent Events (SSE) responses.
Works with **any HTTP method** (`GET`, `POST`, etc.), which makes it compatible
with protocols like MCP that stream SSE over `POST`.
The actual encoding logic lives in the FastAPI routing layer. This class
serves mainly as a marker and sets the correct `Content-Type`.
"""
media_type = "text/event-stream"
def _check_single_line(v: str | None, field_name: str) -> str | None:
if v is not None and ("\r" in v or "\n" in v):
raise ValueError(f"SSE '{field_name}' must be a single line")
return v
def _check_event_single_line(v: str | None) -> str | None:
return _check_single_line(v, "event")
def _check_id_valid(v: str | None) -> str | None:
if v is not None and "\0" in v:
raise ValueError("SSE 'id' must not contain null characters")
return _check_single_line(v, "id")
class ServerSentEvent(BaseModel):
"""Represents a single Server-Sent Event.
When `yield`ed from a *path operation function* that uses
`response_class=EventSourceResponse`, each `ServerSentEvent` is encodedView on GitHub (pinned to 42a41db11f)
Solutions
- Strip/replace line breaks before assigning: `event=name.replace("\r", " ").replace("\n", " ")`.
- Restrict `event`/`id` to slugs/identifiers you control, not arbitrary text.
- Put multi-line content in `data`/`raw_data` instead, which is allowed to span lines.
- Add a unit test that constructs your events with the longest/edgiest inputs.
Example fix
# before yield ServerSentEvent(event="user\nupdate", data=payload) # after event_name = "user_update" # sanitize / slugify yield ServerSentEvent(event=event_name, data=payload)
Defensive patterns
Strategy: validation
Validate before calling
def sse_single_line(value: str | None, field: str = "value") -> str | None:
if value is not None and ("\r" in value or "\n" in value):
raise ValueError(f"{field} must be a single line: {value!r}")
return value
def sanitize_sse_field(value: str | None) -> str | None:
if value is None:
return None
return value.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")
# usage
evt = sanitize_sse_field(user_event_name)
yield ServerSentEvent(event=evt, data=payload) Type guard
def is_single_line(value: str | None) -> bool:
return value is None or ("\r" not in value and "\n" not in value) Try / catch
from fastapi.sse import ServerSentEvent
try:
yield ServerSentEvent(event=name, data=payload)
except ValueError as exc: # pydantic validation on construction
logging.warning("dropping malformed SSE event: %s", exc)
yield ServerSentEvent(event="message", data=payload) # safe fallback Prevention
- Treat `event` and `id` as identifiers/slugs, never free-form text.
- Sanitize any external value used for these fields before constructing the event.
- Put multi-line payloads in `data`/`raw_data`, which permits line breaks.
When it happens
Trigger: Yielding `ServerSentEvent(event="user\nupdate", data=...)` or `ServerSentEvent(id="a\rb", data=...)` from an `EventSourceResponse` path operation. Passing user/DB text into `event` or `id` that happens to contain a newline.
Common situations: Using a free-form string (log line, message body, slug with a trailing newline) as an event name; reading `id` from a source that includes a line break; Windows text containing `\r\n`.
Related errors
- SSE 'id' must not contain null characters
- Cannot set both 'data' and 'raw_data' on the same ServerSent
- {"errors": exc.errors(), "body": body.decode()}
- {"errors": exc.errors(), "body": body.decode()}
- Invalid YAML
AI-assisted analysis of tiangolo/fastapi@42a41db11f (2026-08-04).
Data as JSON: /data/errors/aeae7ed34e07c0af.json.
Report an issue: GitHub.