You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
flm serve: a client that resets before accept stops the server accepting connections (HttpSession ctor throws ENOTCONN) #762
If a client connects and then resets the connection (RST) before flm serve's accept handler runs, the server stops accepting new connections until it is restarted. The listening socket stays open, so new clients complete the TCP handshake into the backlog and then hang.
Cause
WebServer::do_accept counts the connection, builds the session, and only then re-arms itself (server.cpp:568-590):
active_connections_.fetch_add(1);
auto session = std::make_shared<HttpSession>(std::move(socket), *this);
session->start(cors);
...
if (running) {
do_accept();
}
The HttpSession constructor calls the throwing overloads socket_.set_option(...) and socket_.remote_endpoint() (server.cpp:175-186). On Linux, accept() still returns a socket that was reset while it sat in the accept queue, but getpeername() on it fails with ENOTCONN, so remote_endpoint() throws. That exception:
escapes the accept completion handler before do_accept() re-arms, so no further connection is ever accepted;
escapes ioc.run(), ending that I/O thread. The per-thread catch logs Error in WebServer I/O thread: ... (server.cpp:507).
The window is between the connection landing in the accept queue and the handler reaching the constructor. It widens whenever I/O threads are busy, for example during NPU work.
Repro
The socket behaviour, on Linux 7.2:
importsocket, struct, timesrv=socket.socket(); srv.bind(("127.0.0.1", 0)); srv.listen(8)
c=socket.create_connection(srv.getsockname())
c.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0)); c.close()
time.sleep(0.2)
a, _=srv.accept() # succeedsa.getpeername() # OSError: [Errno 107] Transport endpoint is not connected
A deterministic way to hit it in flm serve, with no timing luck needed:
Start flm serve llama3.2:1b and wait for GET /api/version to answer.
kill -STOP <flm pid>. The kernel still completes handshakes into the backlog.
Connect to the port, set SO_LINGER {1, 0}, and close. This queues a reset connection.
kill -CONT <flm pid>.
GET /api/version now hangs, and the log shows Error in WebServer I/O thread: remote_endpoint: Transport endpoint is not connected.
The socket behaviour above was verified on our Strix Halo host. We are measuring the full flm serve sequence now, together with a fix; the code path was read on main at 39ff855.
Suggested fix
In the constructor, use the error_code overloads of set_option and remote_endpoint, and log "Remote endpoint unavailable" on error, as close_connection and read_request already do. The session then ends through the normal read-error path.
In do_accept, also wrap session creation and start() in a try/catch that gives the slot back and falls through to the re-arm. Then no future throw during session setup can stop the accept loop.
Measured through flm serve now (v1.0.6, llama3.2:1b, Strix Halo), using the SIGSTOP / connect+RST / SIGCONT sequence above:
Before the fix: the first cycle hangs. GET /api/version times out, and the log shows Error in WebServer I/O thread: remote_endpoint: Transport endpoint is not connected.
After the fix: 15 of 15 cycles answer 200, past the 10-connection cap, with no I/O-thread errors. A counter-logging debug build shows 32 accepts and 32 releases, so each reset connection gives its slot back once.
Our fix does both parts of the suggestion above:
the error_code overloads in the HttpSession constructor;
a try/catch in do_accept that releases the slot and always re-arms.
Activity
noamsto commentedon Sep 29, 2026
Measured through
flm servenow (v1.0.6, llama3.2:1b, Strix Halo), using the SIGSTOP / connect+RST / SIGCONT sequence above:GET /api/versiontimes out, and the log showsError in WebServer I/O thread: remote_endpoint: Transport endpoint is not connected.Our fix does both parts of the suggestion above:
error_codeoverloads in theHttpSessionconstructor;try/catchindo_acceptthat releases the slot and always re-arms.Patch: https://github.com/noamsto/nix-amd-ai/blob/35a61ad06930603957f9c51ec7bea68f0cc97a5f/pkgs/fastflowlm/patches/accept-loop-rearm.patch
It is layered on our other server patches (including the slot-release fix for #680), so its context lines won't match upstream exactly.
fix(server): never abort FLM readiness probe