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.log finds 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:

  1. 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").
  2. Get a log at Debug level that covers the failure. The window-adjust messages the next step depends on are only written at Debug, and raising the level is not retroactive, you must reproduce the failure after raising it. A log captured at Information will 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.
  3. Look just above the error for the last Remote window size decreased to line, and check whether any Increased window space by line follows it. In a log that was at Debug when the failure happened, nothing following means no SSH_MSG_CHANNEL_WINDOW_ADJUST arrived, 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.
  4. 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 to line 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 call setTimeout) before Connect() / connect(). Changing it on a live connection does not change the window wait for that session. On edtFTPj/PRO, setTimeout must 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 out errors 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:

  1. Remote window size decreased to 0, the connecting client's window is exhausted. Everything now depends on that client sending a window adjust.
  2. Exactly two minutes pass. No Increased window space by line appears.
  3. ConsumeWindowSpace (32768) timed out, your server gives up on the block it was holding.
  4. <path>: read file failed, the download that block belonged to is abandoned and the file handle closed.
  5. Sending SSH_FXP_STATUS [requestid=...,code=4,msg=read error], the connecting client is told the read failed. Code 4 is SSH_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 tiny Increased window space by values 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.ChannelDataWindow and None.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 use Diagnostics.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:

  1. 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 Debug level, 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.

  2. 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.

  3. 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