0X00002775

WSAEDISCON 0x00002775: Graceful Disconnect, Not a Bug

WSAEDISCON means the remote peer closed the connection cleanly. It's a status code, not a fault — most apps just log it wrong.

You're looking at WSAEDISCON (0x00002775, which is decimal 10058) because a TCP peer sent a FIN and your socket operation — usually WSARecv, recv, or a .NET Socket.ReceiveAsync — got told the other side is done. It shows up most often in Windows Event Viewer under Application, in custom log files from line-of-business apps, or as a thrown SocketException with SocketError.ConnectionReset-adjacent noise. The trigger I see most: a client app talking to a load balancer, a firewall, or a database server that enforces an idle timeout. The peer closes gracefully, your code doesn't expect it, and it logs an error that scares everyone at 3am.

What WSAEDISCON actually means

Winsock defines WSAEDISCON as "the remote party has initiated a graceful shutdown sequence." That's the whole story. The remote host sent a TCP FIN, the connection is half-closed, and Winsock is telling you so. It is not a crash, not a network failure, and not something to page on-call for.

Where people get confused is that Winsock is reporting a signal as if it were an error. Microsoft documents this in the Winsock error codes list. If your code path calls WSAGetLastError() and blindly logs the result, you'll see:

WSAEDISCON (10058): A graceful shutdown is in progress.

In .NET, you'll see a SocketException with ErrorCode = 10058 or a wrapped IOException if you're on an SSL/TLS stream. Java NIO throws it as a generic IOException on some JVMs. Node on Windows maps it into ECONNRESET territory, which is misleading.

Why it fires when nothing looks wrong

The classic real-world trigger: an app keeps a persistent connection to a middlebox — F5 BIG-IP, Citrix NetScaler, an AWS NLB, Azure Load Balancer, even a Cisco ASA — and the middlebox has a default idle timeout of 300 seconds. Your app goes quiet for 5+ minutes because it's waiting on a queue. The middlebox sends FIN. Your next read call returns WSAEDISCON. The app logs an error.

Same thing happens with SQL Server connections sitting in a pool, with RabbitMQ heartbeats that don't line up, and with any Windows service talking to a Linux peer where the peer's tcp_keepalive_time is shorter than yours.

The fix

You don't "fix" WSAEDISCON. You handle it. Here's what to actually do:

  1. Stop logging it as an error. In your socket read loop, check the error code before you log. If it's 10058, log it at Info or Debug level, or don't log it at all — treat it as normal connection teardown.
  2. Close your side of the socket. When you get WSAEDISCON, call shutdown(s, SD_SEND) then closesocket(s). If you're in .NET, call socket.Shutdown(SocketShutdown.Both) then socket.Close(). Wrapping in using works too but be explicit if you're logging.
  3. Reconnect if the connection is required. If the socket is a long-lived pipe to a server, reconnect with exponential backoff. If it's a one-shot request, just clean up and move on.
  4. Bump your keepalive. For idle-timeout middleboxes, set TCP keepalive on your socket below the middlebox's idle window. On Windows:
// C: enable keepalive, 60s idle, 10s interval
int on = 1;
setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
struct tcp_keepalive ka = { 1, 60000, 10000 };
DWORD bytes = 0;
WSAIoctl(s, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), NULL, 0, &bytes, NULL, NULL);

In .NET:

socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
// .NET 5+ has finer control:
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 60);
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 10);

That alone kills 80% of these reports in my experience. If your app is the server side and clients are disconnecting after a fixed interval, look at the client's timeout — you won't fix it from your side.

When it's actually a problem

It's a problem if the disconnect happens mid-transaction, mid-file-transfer, or in the middle of a protocol handshake. That means the peer is dying earlier than the protocol expects. Then it's not WSAEDISCON you care about — it's why the peer closed.

  • Check the remote peer's logs for the same timestamp. Look for OOM kills, service restarts, or connection pool evictions.
  • Check for a firewall or IDS in the path. Palo Alto, Fortinet, and Cisco firewalls will silently FIN long-lived sessions when a security profile decides the traffic looks stale.
  • Run a packet capture. Wireshark filter: tcp.flags.fin == 1 && ip.addr == x.x.x.x. Whoever sends the FIN first owns the problem.
Rule of thumb: if the FIN comes from the peer at the exact same interval every time, it's a timeout. If it's random, it's a crash or a middlebox policy reset.

If it still fails

Verify three things in order:

  1. Is the socket actually dead? A FIN means half-close, not full close. You can still send until you get a RST. Some protocols rely on this. Don't tear down prematurely.
  2. Is your error handling catching this correctly? On Windows, 10058 arrives as a normal return from recv, not as a SOCKET_ERROR. If you're checking if (recv(...) == SOCKET_ERROR) log_error(); you're fine — but if you're inspecting WSAGetLastError() unconditionally after every read, you're logging stale errors. That's a bug in your code.
  3. Is something reconnecting too fast? If you reconnect immediately and get WSAEDISCON again within milliseconds, the peer isn't ready. Backoff, don't hammer.

If the error keeps showing up at a fixed interval and the peer logs nothing, the culprit is almost always a stateful firewall or load balancer. Get the network team to check session timeouts on the path between the two hosts. Skip anything that involves reinstalling Winsock or running netsh winsock reset — that's snake oil for this code and it'll just break other things.

Related Errors in Network & Connectivity
0X8000400C Fix CO_E_INIT_TLS_CHANNEL_CONTROL (0X8000400C) – TLS Thread Error 0XC00D0006 Fix NS_E_CANNOTCONNECT (0XC00D0006) streaming error 0XC00D2EF6 NS_E_PROXY_ACCESSDENIED 0XC00D2EF6 Fix – Windows Media Player VPN Split Tunnel Broken: Apps Can't Reach Local Network

Was this solution helpful?

EP
Erropedia Team
Tech Support Editors
The Erropedia editorial team researches and documents real-world tech errors from across Windows, Linux, macOS, networking, databases, cloud platforms, and more. Every solution is reviewed for accuracy and updated as software and systems evolve.