Premature SSE socket closures across projects while upstream completes successfully
faranido
PROOP

2 hours ago

We’re investigating intermittent SSE disconnections between two Railway services in separate projects, communicating over public HTTPS.

On September 14, 2026, our Node webapp received UND_ERR_SOCKET: other side closed while reading Frank’s SSE response. Frank continued processing and completed successfully.

| Webapp failure time (UTC) | Time before failure | Railway request ID |

|---|---:|---|

| 11:35:30.367 | 60.3 seconds | 5wsVpqn9RhqcyGC_2h0iww |

| 11:42:29 | 7.3 seconds | E8LnQVsqTb6isXD42prcFg |

Railway HTTP logs show these requests finishing at approximately 11:35:48 and 11:42:47, respectively, with HTTP 200 and empty upstreamErrors / responseDetails.

Services:

  • Webapp project: a0a519e7-202f-4ddb-a232-5debc2d59a7d

    Service: ada4fc27-6b26-4232-9222-2f1a5d093034

  • Frank project: a6415681-167f-4872-8861-6cfc9d87b2ad

    Service: 6c639b58-ee0b-4935-bd7c-5136bd488d58

Both use the production environment. SSE heartbeats are configured every 15 seconds. We found no service restart during these failures, and our application’s connection timers stop once response headers arrive. A subsequent probe successfully streamed beyond 60 seconds.

Could you correlate these request IDs with edge/Fastly connection termination logs and identify which component closed the connections? Are there additional timeout or buffering settings relevant to SSE across projects?

We found this previous Railway-confirmed Fastly SSE issue, although we don’t know whether it is related.

We also identified an application recovery bug that makes interruptions user-visible; this ticket concerns the underlying premature socket closures.

$20 Bounty

3 Replies

Railway
BOT

2 hours ago

We've looked into this from our side and haven't found anything on the Railway platform that explains what you're seeing, so working it out means digging into your specific setup.

That's exactly what the Railway community is good at, so we'd like to open your thread as a community bounty. Railway pays a bounty to the community member who solves it, and threads like this usually get picked up quickly.

Opening it makes this entire thread public, including everything already posted. Nothing becomes public until you decide. Use the buttons below.

  • Open to the community - Before you click, take a moment to edit or remove anything you'd rather not share. The thread becomes publicly visible right away.
  • Keep it private and close the thread - Nothing becomes public and the thread closes.

Status changed to Awaiting User Response Railway about 2 hours ago


faranido
PROOP

2 hours ago

These disconnections keep happening to us over time. The two requests above are examples of an ongoing reliability issue, and we would appreciate help identifying the underlying cause.


Status changed to Awaiting Railway Response Railway about 2 hours ago


Railway
BOT

2 hours ago

This thread has been opened as a public bounty so the community can help solve it. The thread and any further activity are now visible to everyone.

Status changed to Open Railway about 2 hours ago


Anonymous
FREE

an hour ago

The 60.3s example does not establish a 60-second SSE limit. Railway's current limits distinguish 60 seconds between HTTP/1.1 requests from an active response: HTTP requests can run for 15 minutes while transferring data, with a 5-minute no-data limit. Your 7.3-second failure also needs a different explanation.

The later 200 cannot establish complete delivery to the webapp. I tested a local Node 24.18.0 fixture that closed only a proxy's downstream connection while continuing to read its origin: the client received 200, then UND_ERR_SOCKET, while the origin emitted finish normally. This demonstrates the ambiguity; it does not identify Railway/Fastly as the cause of your failures.

A useful next distinction is whether Frank's actual HTTP response closes before finishing. Attach this before writing headers in its existing Node HTTP/Express handler:

function observeResponse(req, res, emit = console.log) {
  const started = performance.now();
  const requestId = req.headers['x-railway-request-id'] ?? null;
  const record = (event) => emit(JSON.stringify({
    message: 'sse_response', event, requestId,
    at: new Date().toISOString(),
    elapsedMs: Math.round(performance.now() - started),
    headersSent: res.headersSent,
    writableEnded: res.writableEnded,
    writableFinished: res.writableFinished,
  }));
  res.once('finish', () => record('finish'));
  res.once('close', () => record('close'));
}
// At the start of the existing SSE handler:
observeResponse(req, res);

close with writableFinished: false records an unfinished origin response; it does not identify who initiated the closure. A normal finish means data was handed to the OS, not acknowledged by the final client, as Node documents.

On the webapp, record the last actual body-chunk read time, bytes received and nested error.cause codes alongside each request ID. Configured heartbeats don't establish when bytes reached that reader. Undici distinguishes UND_ERR_SOCKET from its own UND_ERR_BODY_TIMEOUT.

Pair those timestamps with Railway's @requestId:5wsVpqn9RhqcyGC_2h0iww and @requestId:E8LnQVsqTb6isXD42prcFg HTTP-log filters. Empty responseDetails is not an end-to-end delivery confirmation; the field is documented for application-failed-to-respond details. Railway support would still need to correlate the private edge connection records to determine the closing component. I don't have access to those records.

Chandan


Welcome!

Sign in to your Railway account to join the conversation.

Loading...