tiangolo/fastapi · error · ValueError

SSE ' ' must be a single line

Error message

SSE '{field_name}' must be a single line

What it means

Raised by `_check_single_line` (fastapi/sse.py:38) as a ValueError when an SSE field ('event', 'id', or other single-line field) contains a carriage return ('\r') or newline ('\n'). The SSE wire format uses newlines as field delimiters, so an embedded newline would corrupt the stream by splitting one event into multiple fields. The validator is attached via `AfterValidator` to the `event` and `id` fields of `ServerSentEvent`.

Solutions

  1. Sanitize the value: strip/replace line terminators before assignment, e.g. `event=event.replace('\r', '').replace('\n', ' ')`.
  2. Use multiple `ServerSentEvent` yields for logically separate lines instead of embedding newlines.
  3. Validate input upstream: reject or truncate strings containing '\n'/'\r' before constructing the event.

Example fix

// before
yield ServerSentEvent(event='update\nrestart', data=payload)
// after
event_name = 'update restart' if '\n' in raw else raw
yield ServerSentEvent(event=event_name.replace('\n', ' '), data=payload)
Defensive patterns

Strategy: validation

Validate before calling

from fastapi.sse import ServerSentEvent

def safe_event(event: str | None = None, **kw) -> ServerSentEvent:
    if event is not None:
        event = event.replace('\r', '').replace('\n', ' ')
    return ServerSentEvent(event=event, **kw)

Type guard

def is_single_line(value: object) -> bool:
    return isinstance(value, str) and '\r' not in value and '\n' not in value

Prevention

When it happens

Trigger: Constructing `ServerSentEvent(event='update\nmore', data=...)` or `ServerSentEvent(id='abc\ndef')`. Yielding an event whose `event`/`id` was built from user input that contains a newline. Multi-line strings assigned to `event` or `id`.

Common situations: Passing untrusted/user-controlled strings into `event` or `id` without sanitization. Building event names from concatenated values that introduced a '\n'. Copying log lines (which contain newlines) into an SSE field.

Related errors


AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11). Data as JSON: /api/errors/aeae7ed34e07c0af. Report an issue: GitHub.

Appendix: 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 encoded

View on GitHub (pinned to 3e8d1526d8)