← Mac Change Notes

What CLOSE_WAIT Means for a Mac App

On a Mac, CLOSE_WAIT means the TCP peer has finished sending, while the local side is still waiting for its application to close. Start by identifying that process in Activity Monitor. One such row alone does not establish a fault.

In this article

Identify the process before trying to fix it

Open Activity Monitor and find the app named in your connection output. Check its process ID, or PID, rather than relying on an abbreviated command name. A browser, sync client, or development tool can have several helper processes.

Apple's process information guide explains how to inspect a process and its open files and ports. If you do not yet know which app owns the connection, start with our Mac app network-connections guide.

When the app is behaving normally, record the observation before changing anything. When it is stuck, save your work and try its normal Quit command before reopening it. Do not force-quit an unfamiliar system process merely because its name appears beside this state.

A successful restart can clear the immediate symptom without identifying its cause. Record the version and what you were doing first, so a recurring problem remains diagnosable.

Use lsof to inspect one PID

In Terminal, replace 12345 with the current PID from Activity Monitor:

/usr/sbin/lsof -nP -a -p 12345 -iTCP -sTCP:CLOSE_WAIT

This command reads socket information. It does not terminate a process, close a connection, or alter network settings.

The options have distinct jobs: -nP keeps addresses and ports numeric; -p selects the PID; -iTCP selects TCP sockets; and -sTCP:CLOSE_WAIT restricts the state. Crucially, -a combines the selections, so the output is for that PID and the requested sockets. The lsof manual documents these selectors.

Look at the PID, file descriptor under FD, and the pair of endpoints under NAME. Repeat the same command after the activity that seemed to trigger the problem. A PID can change when an app restarts, so recheck it before comparing samples.

No output means this query found no matching visible sockets at that moment. It is not proof that the app never used the network. Short-lived states, a changed PID, and visibility restrictions can all limit an observation.

What the state says—and what it does not

TCP allows each direction of a connection to finish separately. The peer sends a FIN when it has finished sending. The receiving side can then be in CLOSE_WAIT until its local application closes that side.

The state therefore points to a stage of connection shutdown. It does not identify the app's purpose, reveal the content sent earlier, or classify the connection as malicious. A short appearance during normal cleanup can be expected. A growing collection that persists while the app stalls deserves closer investigation.

Do not confuse it with TIME_WAIT. That state serves TCP's final shutdown and delayed-segment handling; it is not the same local-application wait. The authoritative definitions and transitions are in RFC 9293, section 3.3.2.

Likewise, LISTEN is a different state. It describes a socket waiting for incoming connections. Our Mac listener-address guide covers that case separately.

A controlled Mac test: EOF did not close the socket

On October 7, 2026, we created two TCP endpoints inside one disposable Python process on macOS 15.7.5, Apple Silicon. Both used 127.0.0.1, so the connection stayed on the Mac. No external server or installed application was tested.

After establishing the connection, we shut down the sending direction of one endpoint. We inspected the other endpoint with the exact lsof filters above, read its end-of-stream result, and finally closed its socket.

StageObserved receiving endpoint
Both endpoints connectedESTABLISHED; no row in the CLOSE_WAIT-only query
Peer stopped sendingCLOSE_WAIT; one matching row
Receiver read EOF but kept the socket openStill CLOSE_WAIT; the same descriptor remained
Receiver closed its descriptorThat descriptor disappeared; no matching CLOSE_WAIT row

The distinction was visible before and after reading EOF. Receiving the end-of-stream indication did not, by itself, close the application's socket. All test sockets were closed afterward.

This demonstrates the mechanism under controlled conditions. It does not establish that a particular commercial app has a leak, how long its cleanup should take, or whether its stalled operation has the same cause.

Decide from repeated evidence, not a single count

There is no universal number of CLOSE_WAIT rows that diagnoses a broken Mac app. A useful investigation connects socket observations to a repeatable action and an actual symptom.

What you observeUseful next step
A row appears briefly and disappearsNote it; avoid changing settings for that observation alone
Similar rows persist, but the app worksCompare later samples and record the activity involved
Rows keep accumulating and the app stops connectingSave evidence, quit normally if possible, and check for an app update
An unfamiliar helper owns the rowsIdentify its parent application before stopping anything

For a support report, collect the app version, macOS version, PID, timestamps, a small relevant output sample, and the steps that reproduce the symptom. State whether restarting the app clears the rows and whether they return. Redact private endpoint names or addresses before sharing logs publicly.

Avoid treating kernel tuning, raised file-descriptor limits, or repeated forced termination as a diagnosis. Those changes can obscure the original pattern. For an app you develop, the next investigation belongs in its socket or connection-pool lifecycle: correlate the observed descriptor with the code responsible for releasing it.

Finally, keep transport state separate from privacy claims. Even an endpoint using port 443 does not show the contents of an exchange or verify its encryption; see what port 443 can tell you. The strongest conclusion from CLOSE_WAIT remains narrow: the peer finished its sending direction, and the local side has not completed closure.