skip to content

In a Server-Sent Events stream, what does a `retry:` field sent by the server tell the client to do?

level: juniorimportance: must knowfreq 58%

answer

  1. the only reconnect lever the server has
  2. changes when, not whether
  3. a default applies before the first one
  4. survives into later reconnects
  5. milliseconds, not seconds

basics

~10 s

A retry field sets, in milliseconds, how long a client waits before re-opening a dropped Server-Sent Events stream. Until the server sends one, the client uses an implementation-defined default of a few seconds.

solid answer

~40 s

`retry:` carries a reconnection delay in **milliseconds**, and it is the only say the emitting side has over the client's automatic re-open. A Server-Sent Events client re-opens a dropped response by itself, with no application code in the path; before any `retry:` arrives it waits an implementation-defined default, commonly a couple of seconds. Once the server writes `retry: 15000`, that client waits fifteen seconds instead, and the value stays in effect for later reconnects on the same stream until another `retry:` replaces it. It changes *when* the client comes back, not *whether* it comes back. Send it early, ahead of the first event, so a client that drops in the first second already has your number rather than its own.

code

http · 9 lines
http
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache

retry: 15000

id: 20480
event: level
data: {"gauge":"upper-weir","metres":3.41}

go deeper

for a junior

Remember two facts: a Server-Sent Events client re-opens a dropped stream on its own, and the retry field is the server telling it how many milliseconds to wait first.

for a middle

Be able to explain that the value persists for the stream rather than for one event, that a default applies until the first one arrives, and that it sets timing only.

for a senior

Show that you pick the number from the cost of re-establishing the feed against how stale the surface may go, and that you raise it deliberately when shedding load.

for a principal

The interesting angle is what a single fleet-wide delay cannot express: identical timing for every client, so spreading return traffic has to be solved somewhere other than this field.

## What a `retry:` line actually sets A Server-Sent Events feed is one HTTP response that the server never finishes. The client issues an ordinary `GET` with `Accept: text/event-stream`, the server answers `200 OK` with `Content-Type: text/event-stream`, and then writes events into the body for as long as the feed lasts. Because it is an ordinary response, it can end at any moment: a laptop lid closes, a radio link out to a river gauge drops in exactly the weather that made the feed worth watching, an intermediary decides a quiet response has been idle long enough. The client does not surface that as a failure and wait for your code to react. It re-opens the request by itself. `retry:` is how the server gets a vote on **when**. - The value is a delay in **milliseconds**. `retry: 15000` means fifteen seconds — not fifteen milliseconds, and not fifteen attempts. - It is a property of the stream as a whole, not of the event it happens to sit near, so it does not have to be repeated on every event. - It affects *when* a client returns, not *whether* it returns: the separate question of which responses tell a client to stop reconnecting altogether is not decided by this field. - The direction is fixed. The **server** writes `retry:`; the **client** obeys it. There is no client-to-server field of this name. ## Before the server says anything A client that has just opened a feed has no delay from you yet. The specification leaves the starting value to the implementation, and in practice it is a small number of seconds. That has two consequences worth thinking about for a district warning console: | situation | delay in effect | |---|---| | Stream opened, no `retry:` seen yet | the client's own implementation-defined default, typically a few seconds | | `retry: 15000` received | fifteen seconds, from then on | | `retry: 15000`, then later `retry: 2000` | two seconds — the most recent value wins | | Response drops and is re-opened | whatever value was last in effect carries over | First, if you never send the field you have accepted whatever the client chose, on every client type that connects to you. Second, because the value survives reconnects, sending it once near the top of the response is enough; you do not have to re-state it after every gauge reading. ## Choosing a number for a real feed The delay trades freshness against load, and both sides of that trade are yours to reason about: 1. **Cheap feed, freshness matters.** A short delay — one or two seconds — means a console that flickers offline for a moment is current again almost immediately. This is the usual choice for a warning surface where a stale threshold crossing is the whole risk. 2. **Expensive feed.** If re-opening means re-authorising, re-reading a snapshot and re-subscribing to a dozen gauges, a one-second delay turns a brief network wobble into repeated expensive work. Lengthen it. 3. **You are shedding load.** Raising the advertised delay before you start closing responses spaces out the return traffic. Note the limit of the mechanism: it is a single number, identical for every client that reads it, so it cannot by itself stop a fleet from coming back in lockstep. Spreading reconnects out per client is a technique that lives above this field, not inside it. ## The three things candidates get wrong The unit is the first: milliseconds, and a value written as if it were seconds is a thousandfold error in the wrong direction. The direction is the second: this is a response field, and a client never sends it. The third is a cross-protocol collision — in an RPC framework a "retry" is a policy for re-issuing a failed call, possibly several times, possibly with backoff. Here it is not a policy and not a count. It is one delay, before one re-open of one stream, of a client that was going to re-open anyway.

  • A stream sends `retry: 20000` at the top and `retry: 2000` an hour later. Which delay is in effect after that?
    Two seconds. The most recent value replaces the previous one for that client's stream, and it keeps applying to subsequent re-opens until another `retry:` field arrives. There is no accumulation and no averaging — the field simply assigns the current reconnection delay.
  • Why send `retry:` before any event rather than alongside the first one?
    Because a response can die in its first second, and a client that never read a `retry:` falls back to its own default. Writing the field as the first thing in the body means even the shortest-lived connection leaves the client holding the delay you chose.

saying these in an interview costs you the question

  • Reads the value as seconds, so the delay is a thousand times off.
  • Thinks a client will not reconnect at all unless the server sends retry.
  • Believes the client sends retry to the server.
  • Treats it as a count of reconnection attempts before giving up.
  • Assumes it must be repeated on every event to stay in effect.
  • Confuses it with an RPC framework's policy for re-issuing a failed call.