skip to content

In a Server-Sent Events feed, what does adding an `event: fault` line change about the block it appears in?

level: middleimportance: should knowfreq 52%

answer

  1. the field that types the block
  2. naming it is optional
  3. unnamed blocks still have a type
  4. the default type is spelled message
  5. the name resets after every dispatch

basics

~20 s

It sets the type of the event that block dispatches, to the literal name fault. It changes nothing else: the payload, the parsing and the blank-line dispatch are identical, and the name applies to that one block only.

solid answer

~40 s

The `event` field types the block. Its value is the event type the client dispatches when the blank line arrives, and a block that names none dispatches the default type, spelled `message`. So the field is not a flag, a filter or a schema declaration - it is a label, compared literally by whatever routes events on the receiving side. It applies to **one block**: the type buffer is cleared after every dispatch, so it never carries over to the next event. That is what lets a single held-open response multiplex several kinds of update - car positions and faults - down one body, with the receiving application routing on the name.

code

http · 7 lines
http
event: fault
data: {"car":"A","code":"DOOR_OBSTRUCTED"}

data: {"car":"A","floor":11,"dir":"down"}

event: fault
data: {"car":"C","code":"OVERSPEED"}

go deeper

for a junior

Know that the event field names the type of one event, that leaving it out is normal, and that the unnamed default type is spelled message. That much answers the screening version.

for a middle

Explain that the type buffer clears after every dispatch, so a name applies to exactly one block, and that a block with a type but no data line dispatches nothing at all.

for a senior

Recognise the silent failure: typed events arrive but nothing routes them, because the receiving side only handles the default type. Diagnose it from the wire, not from the application.

for a principal

Treat type names as a published contract. Decide how coarse they are, how they version, and whether multiplexing several kinds down one response is worth the demultiplexing your consumers now own.

## What the field does, and what it does not A dispatched event carries exactly two things: a **type** and a **payload**. The `event` field sets the first of those. Written as `event: fault`, it says the event this block eventually dispatches is of type `fault`; the value is arbitrary text compared literally, so `fault`, `Fault` and `FAULT` are three different types. It is worth being blunt about what naming a type does *not* change: - It does not change how the block ends. A blank line still dispatches, and only a blank line. - It does not change how `data` lines are assembled into the payload. - It does not appear in the payload. The type travels beside the payload, not inside it. - It does not filter anything on the wire. Every block in the response reaches the client regardless of its type; selecting among them is the receiving application's job. - It does not declare a schema. Nothing validates that two `fault` events have the same shape. ## The default type A block with no `event` line still has a type. The default is the literal string `message`, and it is the type every unnamed block dispatches: | block on the wire | type dispatched | payload | |---|---|---| | `data: {...}` then blank line | `message` | the assembled text | | `event: fault` + `data: {...}` then blank line | `fault` | the assembled text | | `event: fault` then blank line, no data line | nothing dispatched | - | The third row is the one that surprises people: the type buffer alone is not enough to produce an event. If the payload buffer is empty when the blank line arrives, the client dispatches nothing and clears both buffers. The practical consequence of the default is the classic silent failure. An application that only ever handles the default type receives nothing at all from blocks that named one, because those events were dispatched under a different name and no one was listening for it. The events arrived; they were just delivered under a label nothing was watching. ## Per block, never sticky After every dispatch the client clears the type buffer along with the payload buffer. So: 1. A block that names `fault` dispatches type `fault`. 2. The next block, naming nothing, dispatches type `message` - **not** `fault`. 3. A third block naming `position` dispatches type `position`. An emitter that names a type once and then assumes the rest of the stream inherits it is publishing most of its events as the default type without noticing. There is no way to set a type for the response as a whole; if every event is a fault, every block writes `event: fault`. ## Why multiplex types down one response A building-operations console watching a bank of cars wants several kinds of update: a car moved, a door state changed, a fault opened, a fault cleared. The alternatives are one held-open response per kind, or one response carrying typed blocks. The second wins for a reason that has nothing to do with elegance: each held-open response occupies a connection, and connections to one origin are a limited and contended resource for a client. One response carrying four types costs one; four responses cost four. The cost of multiplexing is on the receiving side - it must demultiplex by type - and that is cheap, because the type is already parsed out for it. ## Choosing type names A few properties are worth designing for deliberately: - **Stable.** The name is part of your contract with every receiver; renaming `fault` to `alarm` silently stops delivering to anyone still routing on the old name. - **Coarse enough to route on, fine enough to matter.** One type per kind of update, not one per car. - **Distinct from the default.** Naming a type `message` is legal and means exactly what an unnamed block means, which makes the stream harder to read and gains nothing. - **Free of structure.** The value is opaque text; encoding fields into it (`fault.car-a.overspeed`) puts routing logic into a label instead of the payload. ## Where this goes wrong - Assuming an unnamed block has no type. It has one: `message`. - Assuming a named type persists to later blocks. It never does. - Expecting the name to be matched loosely. It is compared literally, like the field names themselves. - Writing the type into the payload as well and then routing on the payload copy, which quietly makes the wire-level type decorative.

  • One block names a type and the next names nothing. What type does the second dispatch?
    The default type, `message`. The client clears the type buffer after every dispatch, so a name never carries over to the following block. An emitter that wants ten typed events in a row writes the `event` line ten times.
  • Does naming a type change anything about how the block is framed or parsed?
    No. `data` lines still assemble the same way, the blank line still dispatches, the leading-space rule is unchanged, and the type never enters the payload. The field only labels the event that the block was going to dispatch anyway.
  • Can one held-open response carry several different event types?
    Yes, and that is the field's main purpose. Each block is typed independently, so one response can carry position updates, door-state changes and faults, with the receiving application routing on the name. It costs one connection instead of one per kind of update.

saying these in an interview costs you the question

  • Thinks an unnamed block has no event type at all
  • Believes a named type persists until the emitter changes it
  • Expects event type names to be matched case-insensitively
  • Thinks naming a type changes how the block is framed
  • Assumes the event type is delivered inside the payload