tiangolo/fastapi · error · ValueError

Cannot set both 'data' and 'raw_data' on the same…

Error message

Cannot set both 'data' and 'raw_data' on the same ServerSentEvent. Use 'data' for JSON-serialized payloads or 'raw_data' for pre-formatted strings.

What it means

Raised by `ServerSentEvent._check_data_exclusive` (fastapi/sse.py:151, a `model_validator(mode='after')`) as a ValueError when both `data` and `raw_data` are set on the same `ServerSentEvent`. They are mutually exclusive: `data` is always JSON-serialized, `raw_data` is sent verbatim as the `data:` field. Setting both is ambiguous, so FastAPI rejects it at model validation time.

Solutions

  1. Use only `data` when you want JSON serialization: `ServerSentEvent(data=payload)`.
  2. Use only `raw_data` when you want the string verbatim: `ServerSentEvent(raw_data=text)`.
  3. In generic builders, branch on input type and set exactly one of the two fields.

Example fix

// before
yield ServerSentEvent(data={'msg': 'hi'}, raw_data='hi')
// after
yield ServerSentEvent(data={'msg': 'hi'})
Defensive patterns

Strategy: type-guard

Validate before calling

from fastapi.sse import ServerSentEvent

def build_event(*, data=None, raw_data=None, **kw) -> ServerSentEvent:
    if data is not None and raw_data is not None:
        raise ValueError('pass exactly one of data or raw_data')
    return ServerSentEvent(data=data, raw_data=raw_data, **kw)

Type guard

def exactly_one_payload(data, raw_data) -> bool:
    return (data is not None) ^ (raw_data is not None)

Prevention

When it happens

Trigger: Constructing `ServerSentEvent(data={'k': 1}, raw_data='hello')`. Passing both keys in a dict: `ServerSentEvent(**{'data': x, 'raw_data': y})`. Building events generically and accidentally populating both fields.

Common situations: Generic event builders that accept both options. Migrating from `raw_data` to `data` (or vice versa) and forgetting to clear the old field. Copy-paste from an example that set one, then overriding the other.

Related errors


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

Appendix: source

Thrown at fastapi/sse.py:151

        ),
    ] = None
    comment: Annotated[
        str | None,
        Doc(
            """
            Optional comment line(s).

            Comment lines start with `:` in the SSE wire format and are ignored by
            `EventSource` clients. Useful for keep-alive pings to prevent
            proxy/load-balancer timeouts.
            """
        ),
    ] = None

    @model_validator(mode="after")
    def _check_data_exclusive(self) -> "ServerSentEvent":
        if self.data is not None 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 "
                "or 'raw_data' for pre-formatted strings."
            )
        return self


def _split_sse_lines(value: str) -> list[str]:
    # Split on SSE-spec line terminators only (\n, \r\n, \r), preserving
    # trailing empty strings.
    return value.replace("\r\n", "\n").replace("\r", "\n").split("\n")


def format_sse_event(
    *,
    data_str: Annotated[
        str | None,
        Doc(

View on GitHub (pinned to 3e8d1526d8)