TCP Zero Window
The scenario (Why you care)
You’re troubleshooting a “slow” application: file transfers stall, an API call hangs, a database client stops receiving results, or a remote desktop session freezes for seconds at a time. The network link looks up, routing is fine, and there are no obvious drops, yet throughput collapses. In captures, you may see repeated acknowledgments and very little payload movement. This is where TCP flow control often explains the behavior.
A TCP Zero Window event means the receiver is telling the sender: “I cannot accept more data right now.” This is not inherently a network failure. It is backpressure from the receiving host, usually caused by the receiver’s application not reading fast enough or the host being resource constrained. However, zero window patterns can also appear during real faults: a stuck application, a saturated endpoint, or a middlebox terminating and re-originating connections. The goal of this page is to help you decide: is this normal backpressure, or a failure mode you need to escalate?
What “good” looks like (Success pattern)
- What you see
- The receiver’s advertised window shrinks under load, possibly reaching a very small value, and then recovers. You might see short bursts where the sender pauses, followed by continued data transfer. When a true zero window occurs, it is brief, and the receiver later sends a Window Update indicating space is available again.
- Why it happens
- TCP’s receive window reflects how much buffer space the receiver is willing to accept beyond the current ACKed sequence. If the receiving application cannot drain its socket buffer quickly (CPU spike, disk slowness, GC pause, busy event loop), the stack advertises a smaller window to prevent buffer overrun. Once the application catches up, the window grows and the flow resumes.
- Key clue
- Progress resumes after a Window Update: you see payload continue and the advertised window increases. The “pause” is bounded, and you do not see endless probing without recovery.
What goes wrong (Failure pattern)
- What you see
- The receiver advertises window size = 0 for an extended period, and the sender stops sending payload. The sender may transmit small Zero Window Probes periodically to check whether the receiver has opened the window. If the window never opens, the flow looks “alive” (ACKs and probes exist) but makes no application progress.
- Likely causes
- The receiver is genuinely stuck or overwhelmed: the application stopped reading, a process is hung, the host is under memory pressure, or I/O is stalled. Another possibility is that you are not observing the true endpoint: a proxy or load balancer may be the “receiver” in your capture and is backpressuring because its upstream path is unhealthy. In either case, the symptom is the same in TCP: the receiver cannot accept more bytes.
- Key clue
- You see repeated probes with no meaningful window recovery. Time passes with no payload delivery and no increase in the advertised window, even though the connection stays established. That “stable stall” pattern is the deciding signal.
Signals & decision table
The fastest way to diagnose zero window is to classify the direction and decide whether the receiver is temporarily busy or persistently blocked. Use the table below to pick the right next check and avoid chasing unrelated network layers.
| Signal you see | What it suggests | What to check next | Related field/protocol |
|---|---|---|---|
| Receiver advertises 0 window briefly, then Window Update restores flow | Normal backpressure under load | Correlate with receiver CPU/IO spikes; check if bursts align with application behavior | TCP window, ACK stream |
| Long 0 window with periodic Zero Window Probes | Receiver not draining buffers; likely stuck/overwhelmed | Identify which host is receiver in this direction; check whether it is the real app or a proxy | TCP window, TCP keepalive/probe behavior |
| “Window Full” seen frequently while throughput is low | Sender is limited by receiver window (flow-control bound) | Look for small advertised windows and slow window growth; compare to RTT and application response times | TCP window, RTT, ACK pacing |
| Handshake and window look fine, but retransmissions dominate | Loss/congestion instead of flow control | Switch to loss analysis: duplicate ACKs, retransmissions, out-of-order; don’t overfit to window fields | TCP retransmission, SACK (if present) |
| Zero window appears only when crossing a device boundary | Middlebox/proxy buffering or upstream dependency | Capture on both sides of the boundary; compare which side advertises zero window and whether upstream is stalled | NAT/proxy behavior, TCP window |
| Zero window + application sends nothing back for long time | App-level deadlock or blocked response generation | Confirm direction: is the receiver waiting on disk/DB? Consider splitting into “application stall” topic next | TCP stream direction, app payload timing |
How it looks in Wireshark
Display filter example:
tcp.analysis.zero_window or tcp.analysis.window_full
- Wireshark may annotate packets as [TCP ZeroWindow], [TCP Window Full], and [TCP Window Update]. These are analysis hints, not raw header fields, but they are very useful for triage.
- In the TCP details pane, focus on Window size value (and, if present, scaling) plus the ACK progression.
- A classic failure pattern is: long gap in payload, periodic small probe packets, and repeated ACKs with no window growth.
Quick read tip: Identify the receiver for the stalled direction, then watch whether its advertised window ever recovers.
Fast triage checklist
- Pick the stalled direction: which side is trying to send payload when the stall happens?
- Confirm flow control: do you see ZeroWindow or Window Full indicators around the stall?
- Find the receiver: the receiver is the side advertising the window; confirm which IP that is in your capture.
- Look for recovery: do you see a Window Update followed by resumed payload? If yes, treat as backpressure, not outage.
- If no recovery, pivot: determine whether the receiver is the real app endpoint or an intermediate proxy/load balancer.
Common pitfalls
Confusing “Zero Window” with packet loss
A stalled TCP stream can look similar whether the cause is loss or flow control, but the signatures differ. Loss-driven stalls show retransmissions, duplicate ACKs, and out-of-order segments. Zero window stalls show a sender that largely stops sending payload because the receiver told it to stop. If you see clean ACKs and probing with no retransmissions, flow control is a stronger hypothesis than congestion.
Assuming the receiver is always “the server”
Zero window is direction-specific. In uploads, the server can be the receiver (it’s receiving your data), but in downloads the client is the receiver. Also, in modern deployments, the “receiver” in your capture might be a proxy or load balancer, not the final application. Always confirm which host is advertising the window before deciding “who is at fault.”
Missing the role of window scaling
Window scaling is negotiated during the handshake and changes how large windows appear in the UI. You do not need to do the math for P0 triage, but you should avoid comparing raw window numbers across different connections without context. What matters most is the trend: shrinking to zero, staying at zero, and whether updates reopen the window.