Why SFTP transfers fail with "ConsumeWindowSpace timed out", and how to fix it
ConsumeWindowSpace timed out is not a file-size limit, a licence limit or a network fault. It means
one end of an SFTP transfer had no SSH channel window credit left and waited for the other end to
grant more. The grant never arrived, so after a fixed wait the sender gave up. Because large files
are the ones that exhaust a window, it looks like a size limit, transfers stop at 128 KB, or 16 MB,
or 1 GB, or "about 35 minutes in", depending on whose window ran out.
The same flow-control code ships in the CompleteFTP server and in the edtFTPnet/PRO and edtFTPj/PRO client libraries, so an identical-looking message can reach you from three different directions. Almost all the difficulty with this error comes from answering the wrong question for your situation, so start by fixing which situation you are in and then read only that part.
Which situation are you in?
| Case | You are running | The error appears in | The peer that stalled is | Read |
|---|---|---|---|---|
| A | CompleteFTP as the SFTP server | CompleteFTP's Diagnostics.log |
the third-party client that connected to you | Case A |
| B | your own .NET or Java application, built on edtFTPnet/PRO or edtFTPj/PRO, as the client | your application's own log, or an exception thrown into your code | the remote server you connected to | Case B |
| C | CompleteFTP, but the failing transfer is a gateway folder, remote folder or server-to-server link, which makes CompleteFTP the client | CompleteFTP's Diagnostics.log |
the remote server CompleteFTP connected out to | Case C |
Cases A and C write to the same file. A gateway folder's activity carries a log tag ending in
:Gateway, so a line tagged [Session.42:Default Site.22:alice:192.168.1.50:Gateway] is Case C and
the same line without that suffix is Case A. If you administer a server that also has gateway or
remote folders, check the tag before deciding which case applies to you.
Throughout, this article says your CompleteFTP server and the connecting client for Case A,
and your application (or your gateway) and the remote server for Cases B and C. It never
says just the client or the server, because those words point in opposite directions depending on
which case you are in, which is the single most common way this error gets misdiagnosed.
The one rule that decides everything
The end that logs the error is the end that was blocked. The end at fault is always the other one.
Whoever wrote ConsumeWindowSpace timed out was trying to send and ran out of permission to send.
It is the peer, the end that should have granted more credit and didn't, that is misbehaving.
Two consequences follow, and the difference between them is the whole shape of this article:
- The durable fix belongs to the peer. That is why a server administrator staring at their own
Diagnostics.logfinds no server setting that helps: nothing they can change makes the other end grant credit. - The sending end can often stop provoking it, and can survive it. Pipelining less aggressively reduces how hard the peer is pushed, and reconnect-and-resume turns a failed delivery into a pause. That is what Cases B and C give you, not a cure for the peer's behaviour, but a way to live with it. Case A has no such lever, because the sending end there is CompleteFTP itself.
The short version
These four steps are the same in all three cases:
- Work out your case from the table above, and from the message text if you are unsure (see "Identifying your case from the message text").
- Get a log at
Debuglevel that covers the failure. The window-adjust messages the next step depends on are only written atDebug, and raising the level is not retroactive, you must reproduce the failure after raising it. A log captured atInformationwill not contain those lines, and their absence there means nothing at all. For CompleteFTP that is How to enable Debug logging in CompleteFTP for troubleshooting; for Case B it is your library's own logging, set up before you connect. - Look just above the error for the last
Remote window size decreased toline, and check whether anyIncreased window space byline follows it. In a log that was atDebugwhen the failure happened, nothing following means noSSH_MSG_CHANNEL_WINDOW_ADJUSTarrived, and that absence is the diagnosis. If adjusts do follow but carry tiny values, you have a different fault with the same ending. See "Reading the log" under Case A. - Do not just raise the timeout. The wait is for a message that is not coming. A longer timeout only delays the same failure, and in Case A it is not adjustable at all.
Then follow one of:
- Case A: there is no server setting that fixes this; the remedy belongs to the connecting client's owner. Identify which client it is, and tell them what to change.
- Case B: for uploads turn off parallel transfer mode; for downloads set the queued-read limit to 1; then wrap the transfer in disconnect → reconnect → resume, because the libraries' built-in retry does not cover this exception.
- Case C: the same two settings as Case B, but you set them in CompleteFTP Manager under SSH Protocol Tweaks rather than in code.
What the error actually means
(Background, and the same in all three cases. Skip to your case if you only want the fix.)
SFTP runs over an SSH channel, and SSH channels are flow-controlled. Each end tells the other how
many bytes it is willing to receive; that allowance is the window. Every byte sent decreases the
sender's view of the receiver's window. When it reaches zero the sender must stop and wait for an
SSH_MSG_CHANNEL_WINDOW_ADJUST message granting more. Data is never sent to a receiver whose window
is zero.
ConsumeWindowSpace is the internal routine that performs that wait. It logs Remote window size decreased to N on each send, blocks when N would go below the size of the next block, and throws
if the wait exceeds its timeout. So the error always means the same thing:
The peer stopped granting window space, and did not resume within the timeout.
Three things follow, and they are the reason "increase the timeout" so rarely works:
- A healthy transfer never waits. Window adjusts arrive continuously and the wait is measured in milliseconds. When a transfer fails it waits the entire timeout and then throws. There is no middle ground where a slightly longer timeout would have saved it.
- The receiver decides. Nothing the sending end configures makes the receiving end send a window adjust.
- The error can be a symptom, not the cause. If the peer has already vanished, a reset, a firewall idle-timeout, a crash, the blocked sender still sits out its full timeout and reports the window error. Look earlier in the log for the real event.
Identifying your case from the message text
If the routing table above did not settle it, the exact wording will:
| The string you have | Where you found it | Case | What it tells you |
|---|---|---|---|
ERROR ChannelDataWindow ... ConsumeWindowSpace timed out |
CompleteFTP Diagnostics.log, no :Gateway tag |
A | your server was sending; the connecting client stopped granting |
ERROR ChannelDataWindow ... WaitForWindowSpace timed out |
CompleteFTP Diagnostics.log, no :Gateway tag |
A | same cause; this variant fires when the window is already at zero before a block, rather than part-way through one |
either of the above, with a :Gateway tag |
CompleteFTP Diagnostics.log |
C | your gateway was sending; the remote server stopped granting |
EnterpriseDT.Net.Ftp.Ssh.SFTPException: WaitForWindowSpace timed out |
your .NET application, uploading | B | your upload stream waited before writing a block |
EnterpriseDT.Net.Ftp.Ssh.SFTPException: ConsumeWindowSpace timed out |
your .NET application | B | the remote server stopped granting |
ERROR [ChannelDataWindow] ... ConsumeWindowSpace timed out |
the edtFTPnet/PRO library's own log file | B | as above, in the library's log layout rather than as an exception |
java.io.IOException: remote window: waitForWindowSpace timed out |
your Java application | B | the remote server stopped granting |
remote window: waitForWindowSpace called when stopped |
your Java application | B | the channel was closed while a write was blocked, the connection went away first |
ChannelDataWindow is stopped |
either log | A, B or C | the .NET equivalent of the line above; use the log tag to tell which |
The message is written as ConsumeWindowSpace (32768) timed out, where the number is the block size
that could not be sent. When searching a log, search for ConsumeWindowSpace on its own, the byte
count varies with the block, and you want every occurrence.
The message reaching the end user is usually something else entirely, because it comes from
whatever wrapped the exception. Third-party clients have been seen to surface this failure as
Component was busy and got disconnected, as a plain socket timeout, or as nothing at all beyond a
truncated file. Always work from the SFTP log, not the user-facing message.
The wait, and why raising it rarely helps
| Case | What is waiting | Default | Configurable? |
|---|---|---|---|
| A | CompleteFTP server's channel-window wait | 120,000 ms (2 minutes) | No. Compiled in. No site, user or CLI setting changes it. |
| B (.NET) | the edtFTPnet/PRO connection Timeout |
120,000 ms | Yes, SecureFTPConnection.Timeout, in milliseconds |
| B (Java) | the edtFTPj/PRO SSH socket timeout | 60,000 ms | Yes, SSHFTPClient.setTimeout(int) / FileTransferClient.setTimeout(int), in milliseconds |
| C | the gateway connection's Timeout | 120,000 ms | Yes, in the gateway connection editor |
Three traps in that table:
- In Case A the two-minute wait is fixed. The site settings named Timeout for stalled transfers,
Timeout for idle sessions and Timeout for logging in (guide: Limits and Timeouts) are socket
and session timeouts. None of them governs the channel-window wait, and changing them will not move
it. A two-minute gap between the last
Remote window size decreased toline and the error is the signature of this fixed wait, and is how you confirm you are looking at your server's own timeout rather than something else. - In Case B the timeout is captured when the SFTP channel opens. Set
Timeout(or callsetTimeout) beforeConnect()/connect(). Changing it on a live connection does not change the window wait for that session. On edtFTPj/PRO,setTimeoutmust be called before connecting anyway. - A timeout that is too low causes this error. A client-side timeout dropped to 5 seconds has
been seen to produce a burst of
ConsumeWindowSpace timed outerrors immediately. Anything under 30 seconds is too short for a busy peer; 30–120 seconds is the sensible range.
Case A. CompleteFTP is the server
You administer the CompleteFTP server. A third-party client connected to it, your server ran out of window credit while sending, and that client never granted more.
Reading the log
Set the server logging level to Debug, then reproduce the failing transfer, the window
traffic cannot be recovered from a log that was already written at Information. Then read
Diagnostics.log:
- Windows:
C:\ProgramData\Enterprise Distributed Technologies\Complete FTP\Logs\Diagnostics.log - Linux:
/var/log/completeftp/Diagnostics.log
The lines you want come from three loggers, in this order:
2026-07-30 02:14:07,881 DEBUG ChannelDataWindow [Session.4187:Default Site.22:sftpuser:192.168.10.44] Remote window size decreased to 32768
2026-07-30 02:14:07,881 DEBUG ChannelDataWindow [Session.4187:Default Site.22:sftpuser:192.168.10.44] Remote window size decreased to 0
2026-07-30 02:16:07,884 ERROR ChannelDataWindow [Session.4187:Default Site.22:sftpuser:192.168.10.44] ConsumeWindowSpace (32768) timed out
2026-07-30 02:16:07,885 ERROR MessageQueueProcessor [Session.4187:Default Site.22:sftpuser:192.168.10.44] /Outbound/archive-2026-07-30.zip: read file failed
2026-07-30 02:16:07,886 WARN SFTPSubsystemServer [Session.4187:Default Site.22:sftpuser:192.168.10.44] Sending SSH_FXP_STATUS [requestid=8814,code=4,msg=read error]
Reading that from the top:
Remote window size decreased to 0, the connecting client's window is exhausted. Everything now depends on that client sending a window adjust.- Exactly two minutes pass. No
Increased window space byline appears. ConsumeWindowSpace (32768) timed out, your server gives up on the block it was holding.<path>: read file failed, the download that block belonged to is abandoned and the file handle closed.Sending SSH_FXP_STATUS [requestid=...,code=4,msg=read error], the connecting client is told the read failed. Code 4 isSSH_FX_FAILURE.
Then one of two things happens. Usually the connecting client sees the failure and closes. Sometimes the status message itself cannot be sent, because the window is still zero, and you get:
2026-07-30 02:18:07,889 ERROR MessageQueueProcessor [Session.4187:Default Site.22:sftpuser:192.168.10.44] Failed to send status message
after which the server disconnects the channel. If you see Failed to send status message you have
waited two full timeouts, four minutes, for one stuck transfer, which is why these sessions appear
to hang on the server long after the client has given up.
Two things worth knowing while reading a server log:
Remote window size decreased to 1, repeatedly, with tinyIncreased window space byvalues is a different fault with the same ending. The peer is granting credit, but in increments of tens or hundreds of bytes, so writes crawl and something eventually times out. That is a defect in the connecting client, not a tuning problem, the increments should be tens of kilobytes.- In CompleteFTP Manager's Real-Time Logging tab the logger names are prefixed with a category,
so the same lines read
SSH.ChannelDataWindowandNone.MessageQueueProcessor.None.simply means that logger has no category mapping; it is not an error. Real-time lines also carry no session tag, so for anything involving more than one session useDiagnostics.log.
What to do
There is no server-side setting that fixes this. The window size (just under 1 MB), the maximum packet size (32,700 bytes, or the client's advertised maximum if smaller), the SFTP message-queue depth (200) and the two-minute wait are all compiled in. Nothing in the manager, the CLI or JSS exposes them. Do this instead:
-
Identify the connecting client from the session tag and get its version. The tag gives you the user name and address, and earlier in the same session, at
Debuglevel, the server logs the client's own identification string:2026-07-30 02:11:44,102 DEBUG SFTPConnection [Session.4187:Default Site.22:sftpuser:192.168.10.44] Client version = 'SSH-2.0-SomeClient_1.4.66'That string, and nothing else in the log, tells you what to ask the client owner to change.
-
Tell the client owner what to fix. In every case support has resolved, the connecting client was setting an initial window and then never adjusting it, or adjusting it far too late. The fix is theirs: upgrade the client library, or reduce how aggressively it pipelines requests. A useful proof for a sceptical partner is that the same file transfers cleanly using OpenSSH
sftp, WinSCP or FileZilla from the same network, those clients manage the window correctly.If the client owner is using edtFTPnet/PRO or edtFTPj/PRO, send them Case B; it is written for them.
- Ask for resume-capable retries. A client that reconnects and resumes turns this failure into a pause rather than a failed delivery. Well-behaved GUI clients already do it, which is why they appear immune.
If the connecting client is SunOS or Solaris sftp and you have done all of the above, report it to
support rather than working around it, that combination has needed server-side changes before.
Case B. Your application is the client
You wrote a .NET or Java application that uses edtFTPnet/PRO or edtFTPj/PRO to transfer files to somebody else's SFTP server. Your application ran out of window credit while sending, and the remote server never granted more. Nothing in this section is a CompleteFTP setting, and it applies whether or not the remote server happens to be CompleteFTP.
Reading your application's log
The libraries use their own log layout, not the server's:
ERROR [ChannelDataWindow] 30 Jul 2026 02:16:07.884 : FTPConnection.4 ConsumeWindowSpace (32768) timed out
FTPConnection.N identifies the connection rather than a server session. Look for the same sequence
as in Case A, the last Remote window size decreased to line, then the absence of any
Increased window space by line.
Work through the four steps below in order. The first two address the two known client-side behaviours that provoke a server into stalling; the third is the one that actually makes transfers survive.
1. Uploads: turn off parallel transfer mode
Parallel mode is on by default and applies to uploads only, it pipelines writes. Some servers lose track of window allocations under it.
// edtFTPnet/PRO: set before Connect()
ftp.ParallelTransferMode = false;
// edtFTPj/PRO
client.getAdvancedSSHSettings().setParallelWriteMode(false);
// or, on SSHFTPClient directly:
sshFtp.setParallelMode(false);
Downloads are unaffected by this setting, so if your failure is on a download, skip to the next step.
2. Downloads: stop pipelining reads
The read-request queue defaults to 32. Setting it to 1 makes reads synchronous. It is slower,
sometimes markedly so, but it is the documented remedy for servers that mishandle pipelined reads,
and the same servers often produce FXP_READ mismatch. Requested: 65535 received: 32768 as well.
// edtFTPnet/PRO: set before Connect()
ftp.SSHMaxQueuedReadRequests = 1;
// edtFTPj/PRO
client.getAdvancedSSHSettings().setMaxQueuedReadRequests(1);
3. Catch it, reconnect, and resume
The libraries will not retry this for you. On edtFTPnet/PRO, RetryCount (default 3) retries
only IOException, SocketException, MalformedReplyException and ObjectDisposedException. A
window timeout is an SFTPException, which derives from FileTransferException, not from
IOException, so it falls straight through the retry logic to your code. On edtFTPj/PRO the SFTP
client has no retry loop at all.
So for any transfer large enough to matter, implement it yourself:
// edtFTPnet/PRO
for (int attempt = 1; attempt <= 5; attempt++)
{
try
{
ftp.UploadFile(localPath, remoteFile,
attempt == 1 ? WriteMode.Overwrite : WriteMode.Resume);
break;
}
catch (SFTPException)
{
ftp.Close(true); // abrupt close: releases the remote file handle
Thread.Sleep(5000);
ftp.Connect();
}
}
The Java form goes inside the same loop. Catch java.io.IOException, and use quitImmediately()
and connect() where the .NET example uses Close(true) and Connect():
// edtFTPj/PRO: inside the same retry loop, so `attempt` is the loop counter
client.uploadFile(localPath, remoteFile,
attempt == 1 ? WriteMode.OVERWRITE : WriteMode.RESUME);
Four details that matter more than the loop itself:
- Disconnect before you resume. Many servers hold a lock or an open handle on the partial file until the connection closes. Reconnecting on the same session and reopening the file produces a cascade of "file not found" or zero-byte-file failures instead of a clean resume.
- Use
Close(true)/quitImmediately()when the connection is already stuck. The graceful close waits for a reply that may never come, costing you another full timeout. - Resume only works in binary mode, and only if the remote server supports writing at an offset. If it does not you will see an error from the remote server about seeking or an unsupported offset; in that case the transfer has to restart, and the loop should say so rather than silently overwriting.
- Progress counters restart at zero on a resume. The libraries report bytes transferred for the current operation only. If you display total progress, add the resume offset yourself.
4. Only then, look at the timeout
If you have done the above and the transfer still fails, a timeout of 30–120 seconds is reasonable. Raising it far beyond that does not help: it converts a fast failure into a slow one. Raising it is appropriate only when the log shows window adjusts still arriving, just late.
Case C. CompleteFTP is the client
Gateway folders, remote folders and server-to-server links make CompleteFTP an SFTP client, reaching
out to somebody else's server. That path uses the same edtFTPnet/PRO stack as Case B, so the causes
and remedies are the same, but you set them in CompleteFTP Manager instead of in code, and the error
lands in your own Diagnostics.log rather than in an application log.
Confirm you are in this case, not Case A, by the :Gateway suffix on the log tag. Then edit the
folder's connection in CompleteFTP Manager and open the SSH Protocol Tweaks category:
| Setting | Use it for | Case B equivalent |
|---|---|---|
| Parallel transfer-mode | turn off for uploads to a remote server that loses track of window credit | step 1 |
| SSH max. size of read-request queue | set to 1 for downloads from a remote server that cannot cope with pipelined reads | step 2 |
| SSH window-size | leave alone unless you know the remote server's behaviour | , |
| SSH max. packet-size | leave alone; lowering it has been tried and rarely helps | , |
| Send initial window adjust | turn off only for remote servers that reject the initial adjust | , |
| Timeout | 30,000–120,000 ms, the field is in milliseconds, as the table above shows; not a fix, but avoid values below 30,000 | step 4 |
These are not described in the product guide. Change one at a time and retest.
There is no gateway equivalent of Case B step 3, you cannot supply your own retry-and-resume loop around a gateway transfer. If the remote server stalls persistently, the durable fix is with its owner.
When it doesn't work
Each item is tagged with the cases it applies to. Ordered by how often it comes up in support cases.
1. You raised the timeout and nothing changed. (all cases) Expected. See "The wait, and why raising it rarely helps", in Case A it is not adjustable at all, and in every case the wait is for a message that is not coming. If successful transfers wait milliseconds and failed ones wait the exact timeout, the timeout is not your variable.
2. Upgrading did not fix it. (all cases) ConsumeWindowSpace timed out is not a single bug.
It is the generic report of a stalled window, and it still occurs between fully up-to-date software
when one end manages its window badly. If both ends are current and you still see it, stop upgrading
and start reading the log to find out which end stopped granting credit.
3. It works with WinSCP or FileZilla, so it must be your product. (Case A) Those clients reconnect and resume automatically when a transfer breaks, so the user never learns it broke. That is a difference in error handling, not in protocol correctness. It is also the strongest argument for implementing Case B step 3 in whatever client is failing.
4. The Java library works and the .NET library fails with identical settings (Case B), or the
reverse. The two stacks pipeline differently and use different defaults, the Java client's default
timeout is 60 seconds against .NET's 120, and the Java client's channel windows are fixed at 2 MB and
are not settable, whereas .NET exposes SSHWindowSize. A remote server that mishandles window credit
can be provoked by one and not the other. It does not tell you which library is wrong.
5. The real error was earlier in the log. (all cases) If you find
An existing connection was forcibly closed by the remote host, a TCP reset, a key re-exchange
failure or an authentication storm above the window error, that is your cause and the window error is
the aftershock. This is common enough that it is worth grepping for those first.
6. The transfer failed after all the bytes were sent. (Cases B and C) If the data went across
and the failure is on the close or the rename, you are probably looking at a different timeout, see
"This is not your error" below, and the fix is Close(true) / quitImmediately() rather than any
window setting.
7. The session stays open on your server after the failure. (Case A) Two consecutive two-minute waits, plus whatever the connecting client does next, can leave a session visible for minutes. If sessions never clear at all, that is a separate problem: set Timeout for idle sessions on the site (guide: Limits and Timeouts), which does apply to idle sessions even though it does not apply to the window wait.
8. SCP, not SFTP. (Cases B and C) SCP uses the same channel and the same window, so it produces the same error, typically with the file truncated at a multiple of the transfer buffer. The same remedies apply. If you hit it on a small file, report it, that is not expected behaviour.
9. You are looking at the wrong log. (all cases) The two ends describe the same conversation from opposite sides, and only one of them contains the answer. If the log in front of you shows your end blocked, no amount of tuning how your end sends will help, it is the peer's receiving behaviour that has to change.
If you are on an older release
Two things to know if you cannot upgrade immediately. The read-request queue control that Case B step 2 relies on does not exist before edtFTPnet/PRO 10.0.0 or edtFTPj/PRO 7.2.0, on earlier builds pipelining cannot be switched off at all. And server-side window-space defects were fixed in CompleteFTP 11.0.4 and 12.1.2, so for Case A confirm you are on 12.1.2 or later before investigating the connecting client.
This is not your error
Several nearby SFTP failures are routinely mistaken for this one. They have different causes and different fixes:
| Error | What it actually is |
|---|---|
GetMessage timed out (request id=...) |
waiting for a reply to a specific SFTP request, not for window credit. Governed by your application's Timeout. Common on SSH_FXP_CLOSE at the end of a large upload. |
Protocol negotiation timeout |
the connection never got past the SSH version exchange. Nothing to do with transfers. |
The socket is EOF / The Transport Protocol thread failed: The socket is EOF |
the peer closed the connection without an SSH disconnect, usually during key exchange. |
FXP_READ mismatch. Requested: 65535 received: 32768 |
a server that mishandles pipelined reads. Same remedy as Case B step 2, different symptom. |
Timed out waiting for packet from handler |
a client-side message-handling timeout during connection setup. |
java.lang.OutOfMemoryError: Java heap space on a very large transfer |
usually an event collector accumulating messages, not flow control. |
If your log has one of those and a window error, treat the other one as the cause.
Related
- Product guide: SFTP/SCP/SSH settings, Limits and Timeouts, Gateway folders, Gateway configuration, How to view the server log files, Getting server diagnostics, How to get diagnostics for support, How to produce clean log files, Performance.
- KB: How to enable Debug logging in CompleteFTP for troubleshooting, how to get the log this article tells you to read.
- For background on client-side automation, including throughput settings and retry-and-resume as a design choice rather than a fix, see SFTP automation.
Other technical articles
- CompleteFTP service fails to start after an upgrade: how to find the cause and fix it
- CompleteFTP: System.OutOfMemoryException during SFTP, or a service that stops responding
- Does edtFTPnet/PRO support .NET 8? Which assembly to reference for .NET Framework, .NET Core and .NET 5+
- How to disable weak ciphers and encryption algorithms in CompleteFTP
- How to enable Debug logging in CompleteFTP for troubleshooting
- How to export and import CompleteFTP configuration between servers and versions
- IP filtering and whitelisting in CompleteFTP: what it supports and how to configure it
- Passive FTP and FTPS data connection failures in CompleteFTP: login works, the directory listing times out
- SFTP login fails with "No supported authentication methods available (server sent: publickey)" or "Permission denied (publickey)"
- Using Active Directory, Entra ID (Azure AD), SAML and LDAP groups to control who can log in to CompleteFTP
- Why an SSL certificate won't import into CompleteFTP, and how to fix it