Passive FTP and FTPS data connection failures in CompleteFTP: login works, the directory listing times out
The user logs in successfully, and then nothing happens. The connecting client sits for a few
seconds on the first directory listing or the first transfer, gives up, and reports
Connection timed out after 20 seconds of inactivity and Failed to retrieve directory listing.
SFTP to the same machine is fine. Sometimes it works from inside the network and fails from outside, or the other way round, or it
worked yesterday and does not today.
FTP and FTPS use one connection for commands and a second, separate connection for every listing and every transfer. Logging in only proves the first one works. Everything below is about the second.
Throughout, the CompleteFTP server means the machine running CompleteFTP, and the connecting client means the FTP or FTPS program making the connection, whichever end you happen to sit at.
Which situation are you in?
| Case | Which end do you administer? | What you are able to open | Read |
|---|---|---|---|
| A | The machine running CompleteFTP | The session transcript, plus CompleteFTP Manager or the CLI, and the CompleteFTP server's log files | Case A |
| B | Only the connecting end | The session transcript, and your own egress firewall | Case B |
Both cases see the same session transcript, so the transcript is not the discriminator. What separates
them is whether you can open Diagnostics.log on the CompleteFTP machine: the passive listener writes
there, and nothing in that transcript tells you what it said. If you administer both ends, for
example while testing from a workstation on the same LAN, read Case A.
The short version
These two steps belong to both cases.
- Get the connecting client's transcript as far as the passive reply. A healthy exchange reads
227 Entering Passive Mode (a,b,c,d,p1,p2).followed by150 Opening BINARY mode data connection for listing, and then stops. The reply carries the addressa.b.c.dand the portp1 × 256 + p2. - Decide whether the data connection reaches the CompleteFTP server at all. Nothing else in this
article matters until you know the answer, and each case has its own way of finding out. In Case A,
look for the
OnConnectedline in the server log (see Reading the log); if it is there, the connection arrived. In Case B, the transcript answers it: first check whether the address in the227reply is a private one while you are off-site, which is a diagnosis on its own, and if the address is right, note how the data connection failed. A connection refused straight away means something answered and rejected it; a wait that ends in a timeout means the packets were dropped in the path. Most clients distinguish the two in their own log.
Then:
- Case A (below): set a passive port range and open that range inbound on every
firewall and NAT device in front of the CompleteFTP server. Set the external IP address as well if
the server is behind NAT and your clients send
PASVand use the address they are given. Clients that sendEPSVare never given an address, and manyPASVclients ignore it and reuse the control connection's address, so a NAT'd server often works without it. Check a transcript before assuming either way. - Case B (below): if the address is a private one and you are off-site, the fix is at the CompleteFTP server end and there is a specific thing to ask for. If the address is right and the port is refused or silent while the session is still waiting, the block is in the path, and switching the connecting client to EPSV is the quickest test.
How a passive data connection is set up
Five facts decide every failure in this article. All five come from CompleteFTP's own behaviour, not from the protocol in general.
- The listener binds to the interface the control connection arrived on, not to all interfaces. On a multi-homed server the data port only exists on that one address.
- The port comes from the configured passive port range. With no range configured CompleteFTP asks the operating system for any free port, which is why an unconfigured server produces a different, unpredictable port every time.
- The
227 Entering Passive Modereply carries an address and a port. The port is always the one the listener actually bound. The address is the interface address, unless the external IP address setting overrides it. The229reply toEPSVcarries the port only, and the connecting client is required to reuse the control connection's address. - One session uses one passive port, not one per file. The listener is created on the first
PASVorEPSVand reused for every subsequent transfer in that session, keeping the same port throughout, so the range has to cover concurrent sessions. The port is returned to the range when the session ends, or when the connecting client switches to active mode withPORTorEPRT. -
The data connection must come from the same source address as the control connection, or from an address in the passive IP white-list. Anything else is rejected after the TCP connection is accepted, which looks exactly like a hang to the connecting client. The server logs the rejection:
Data channel IP <data address> matches neither control channel IP <control address> nor any white-listed IP (<white-list>)
The settings, where they live, and what they default to
In CompleteFTP Manager: Sites → select the site → FTP / FTPS → Advanced FTP/FTPS Settings → Passive (PASV) Transfer Settings. Both of those groups are collapsed when the panel opens, which is the usual reason people report the settings are missing. The passive-wait timeout is elsewhere, in Limits and Timeouts on the same panel.
| Setting (Manager caption) | CLI / JSS property | Default when never set | What it changes |
|---|---|---|---|
| Minimum port number, Maximum port number, under Port Range | pasvPortMin, pasvPortMax |
both unset, giving the range [0,0], meaning an operating-system-assigned port |
which ports the data listener binds |
| External IP address (IPv4 only) | pasvIP |
unset, so the reply carries the interface address the control connection arrived on | the address in the 227 reply, and nothing else |
| Use external IP address for | redirectPassiveIPSecureOnly |
FTP/FTPS, that is, false |
whether the external IP address is also used for unencrypted FTP, or for FTPS only |
| IP white-list (in addition to client IP) | pasvWhiteList |
empty, shown as Only client IP allowed | which additional source addresses may open the data connection |
| Timeout for PASV wait (seconds), under Limits and Timeouts | timeoutPassiveWait |
unset, meaning CompleteFTP waits indefinitely | how long CompleteFTP holds the port waiting for the connecting client |
Two things about that table are easy to get wrong.
- The Manager shows the timeout in seconds and stores it in milliseconds. Every timeout on that
panel does this. The CLI and JSS property is the stored value, so
timeoutPassiveWait=60is sixty milliseconds, and sixty seconds istimeoutPassiveWait=60000. - The port range is a range of individual ports, and the recommended size is 100. A range between 1024 and 65535 avoids the privileged ports.
From the command line (same on Windows and Linux):
completeftp site show "Default Site" pasvPortMin pasvPortMax pasvIP pasvWhiteList redirectPassiveIPSecureOnly timeoutPassiveWait
completeftp site set "Default Site" pasvPortMin=50000 pasvPortMax=50099
completeftp site set "Default Site" pasvIP=203.0.113.10 redirectPassiveIPSecureOnly=true
completeftp site set "Default Site" pasvWhiteList=10.20.0.7,10.20.0.8
site set applies the change itself, with no separate apply step. In the Manager you must click
APPLY CHANGES or the change never reaches the CompleteFTP server. The CLI is the administration
interface on Linux and works as installed; on Windows it is off by default and has to be enabled on
the service. The same property names are used from JSS administration scripts, which require
Enterprise MFT and an explicit config.applyChanges().
Case A: you administer the CompleteFTP server
1. Set a passive port range
Without one, the server binds whatever port the operating system hands it, and no firewall rule can
be written for that. Pick a block of about 100 ports above 1024 that nothing else on the machine
uses, and set both ends. Setting only one end is worse than setting neither: with a minimum and no
maximum the range is empty, every PASV fails, and the client gets 501 Internal error. Contact server administrator.
2. Open the range inbound, on every device in front of the server
Every hop needs the rule: the host firewall on the CompleteFTP machine, the perimeter firewall, and any NAT or port-forwarding device. The rule is inbound to the server, for the whole range, in addition to port 21 (or 990 for implicit FTPS).
For plain FTP many firewalls read the PASV exchange and open the data port for that one connection
by themselves, which is why plain FTP can work with no range configured at all. For FTPS the
commands are encrypted, the firewall cannot read them, and the range has to be opened explicitly.
A server that works over FTP and times out over FTPS, with everything else identical, is this and
almost nothing else.
3. Set the external IP address only if you need it
Set it when the CompleteFTP machine has a private address and clients reach it through NAT on a
public one. Leave it unset otherwise. It changes the address in the 227 reply and nothing else,
so it never affects EPSV, IPv6, or SFTP.
The cost of setting it is that clients on the same LAN are then told to connect their data channel to the public address, which from inside the network frequently does not work. Three ways out, in the order worth trying:
- Leave it unset and check whether your clients need it. A client that sends
EPSVis never given an address, and many clients that sendPASVignore the address in the reply and reuse the control connection's address instead. Either behaviour makes a NAT'd server work with nothing configured. Confirm from a transcript before relying on it. - Set it to FTPS only. Choose
FTPS onlyfor Use external IP address for. Unencrypted FTP then keeps the LAN address in the reply, which is also what NAT routers that rewrite thePASVreply themselves expect to find. When this setting suppresses the override, the server logsPASV external IP address set but not used as configured for FTPS onlyat Information level. - Run a second site on a different port for internal users, with the external IP address unset. Multiple sites require Enterprise MFT.
The field accepts a literal IPv4 dotted-quad only. A host name, an IPv6 address or a value with
stray whitespace is not accepted by the Manager field, and if such a value is written through the
CLI or JSS the server logs PASV external IP address setting is invalid: and falls back to the
interface address.
4. Apply, then confirm from the server's own log
Raise the server logging level first, because the lines that show the range and the bound port are
written at Debug and raising the level is not retroactive. In CompleteFTP Manager that is the
Monitoring panel → Log Files tab → Server logging level, followed by APPLY CHANGES;
from the command line it is completeftp monitor set logging.level=Debug, which applies itself.
Reconnect a client, read the log as described in Reading the log, then put the
level back to Information.
Case B: you connect to a CompleteFTP server someone else administers
You cannot change anything on the server, so the job is to establish which of the two ends is at fault and hand over something specific.
- Read the address out of the
227 Entering Passive Mode (a,b,c,d,p1,p2).reply. Ifa.b.c.dis a private address (10.x,172.16.xto172.31.x,192.168.x) and you are connecting from outside that network, the server is advertising an address you cannot route to. Some clients notice and sayServer sent passive reply with unroutable addressbefore falling back to the control connection's address. - If your client fell back to the control address and the transfer still timed out, the address was not the problem. The port is blocked somewhere in between. Go to step 4.
- Try EPSV. Most clients have a setting for it. The
EPSVreply carries no address, so if the listing succeeds underEPSVand fails underPASV, the address in the227reply is the fault and there is nothing wrong with the port. Rule out an FTP-aware firewall on your own side first, because one that rewrites or mangles thePASVreply produces the same signature. Then report the result to the server's administrator: it is exactly what they need to know. - Check your own outbound rules for the whole passive range. Your firewall has to allow outbound connections to arbitrary high ports on the server. For FTPS it cannot infer them by reading the control connection, so a rule that permits port 21 only is not enough. Ask the server's administrator which range is configured, and allow that.
- Active mode is a fallback, not a fix. Your client can send
PORTinstead, which makes the server connect back to you, and that needs your own firewall to permit a connection arriving at your machine. Behind corporate NAT it usually cannot work. Those connections back to you normally leave the server from port 20, though the server falls back to an operating-system port if it cannot reuse port 20, so do not key a rule on the source port alone.
What to ask for, if the evidence points at the server end: a passive port range configured on the site and opened on the firewall for connections arriving at the server, and, if the server is behind NAT, the external IP address set, ideally for FTPS only. If your own outbound path uses a pool of source addresses, for example a cloud firewall or an SNAT pool, send the administrator the full list of addresses that pool can present. They need every one of them, for the reason given in When it doesn't work, item 5.
When it doesn't work
Ordered by how often each appears in support tickets. Each is tagged with the case it applies to.
1. No passive port range is configured (Case A). CompleteFTP hands out a different unpredictable
port on every listing, so a firewall rule cannot exist. In the log at Debug, this is
PASV port range [0,0]. Fix it as in Case A step 1.
2. The external IP address is set, and now the LAN cannot connect (Case A). The mirror image of the problem it was set to solve, and the single most common regression after a configuration change. Internal clients are told to open the data channel to the public address. Removing the setting fixes the LAN and breaks remote clients again, which is why the three options in Case A step 3 exist.
3. The range is open on one firewall but not on all of them (Cases A and B). On the server side: host firewall, perimeter firewall, cloud security group, NAT device. On the connecting side: your own egress firewall, which is the hop a Case B reader owns. The connection is silently dropped at whichever hop lacks the rule, and every hop produces an identical timeout at the connecting client.
4. The passive port range is exhausted, and CompleteFTP allocates outside it (Case A). Each
concurrent session holds one port for its whole life, and the port is only returned when the session
ends. Once every port in the range is in use, the next session gets an operating-system-assigned port
instead, outside the range and therefore blocked by the firewall rule you wrote. The Debug log
shows Next passive port = 0 followed by Bound to <interface address> on port 0, while the
connecting client is sent a real, out-of-range port. Size the range for peak concurrent sessions,
not for files: a session that uploads 500 files still uses one port. A session that is abandoned
rather than closed holds its port until the connection is torn down, so set an idle-session timeout
on a busy server: Timeout for idle sessions (seconds) under Limits and Timeouts, or
timeoutIdle from the CLI, which like every timeout there is stored in milliseconds.
5. The data connection arrives from a different address than the control connection (Case A).
An Azure Firewall, a load balancer, an outbound proxy or an SNAT pool at the connecting client's
end can use one address for the control connection and another for the data connection. CompleteFTP
accepts the TCP connection and then rejects it, logging a message containing
matches neither control channel IP.
The remedy is the IP white-list (in addition to client IP) setting: add every address the other
end can present. It takes literal IP addresses only, comma-separated, with no ranges and no CIDR, so
a pool of addresses has to be listed in full. An address in that white-list also bypasses IP
filtering and auto-banning for the data connection, so keep the list to addresses you trust.
6. Connection attempt on PASV port 50017 without PASV sent - disconnecting (Case A). Something
connected to the passive port when CompleteFTP was not expecting a data connection. The listener
stays bound to its port for the whole session but only accepts one data connection per PASV or
EPSV, so this is what a second connection to that port looks like. Two of the common sources are yours to
stop: a firewall or security appliance that probes the port after seeing it advertised, and a
load-balancer health check aimed at the passive range. The third, a client that opens the data
connection twice, has to go back to whoever runs that client. It is a warning, not the cause, unless
it appears in place of the transfer rather than alongside it.
7. The router rewrites the PASV reply for FTP but cannot for FTPS (Cases A and B). Some NAT
routers replace the LAN address in the reply with the external one themselves, and some of those
terminate the connection when they do not find a LAN address to replace. This is what
Use external IP address for = FTPS only is for: the router keeps rewriting unencrypted FTP,
and FTPS gets the hard-coded address it cannot see. The remedy is a server-side change, so a Case B
reader asks for it rather than making it.
8. The IP filter or auto-ban drops the data connection (Case A). Both are applied to the passive data connection as well as the control connection. A filter that permits the connecting client's address but not the address its data connection comes from, or an auto-ban triggered by repeated failures, drops the data connection and only writes a line at Information level.
9. The CompleteFTP server is multi-homed and the connecting client reached an interface you did not expect (Case A). The listener binds only to the interface the control connection arrived on, so the firewall rule has to allow the range on that interface. Check the Listening IP addresses setting for the site.
10. Failed to bind port 50004 on interface 10.0.0.5 in the log (Case A). Another process holds
that port. CompleteFTP tries the next port in the range and carries on, so a few of these are
survivable, but a range that overlaps the operating system's dynamic port range will produce them
continuously. If every port in the range fails, the connecting client gets a 501 and the log
carries No available ports in the range.
11. Timed out waiting for RETR/STOR/LIST/APPE command (Case A). The connecting client opened
the data connection and then never sent the command it was for. This only occurs when
Timeout for PASV wait has been set, and the units trap in the settings table above is the usual
explanation: a value intended as seconds was stored as milliseconds and now expires immediately.
12. One client works and another does not against the same server (Cases A and B). Almost always
EPSV versus PASV, or a client that substitutes the control connection's address for the one in
the reply while another obeys the reply literally. Compare the two transcripts at the 227 or 229
line before changing anything on the CompleteFTP server.
Reading the log
Server logs are in <dataDir>\Logs on Windows, where <dataDir> is
%ProgramData%\Enterprise Distributed Technologies\Complete FTP, and in /var/log/completeftp on
Linux. Diagnostics.log has everything; Errors.log carries Warning level and above, so the two
lines that matter most reach it without raising the logging level at all.
Visible at the default Information level:
2026-08-03 09:14:02,551 WARN PassiveListener [Session.41:Default Site.21:ftpuser:192.168.1.50] The client instructed the server to await an incoming passive data connection, but it never arrived. Is a router or a firewall blocking connections? Read more at https://enterprisedt.com/products/completeftp/doc/guide/howtofirewalls
2026-08-03 09:14:02,551 WARN PassiveListener [Session.41:Default Site.21:ftpuser:192.168.1.50] Connection attempt on PASV port 50017 without PASV sent - disconnecting
The first line is the diagnosis for most of this article: CompleteFTP bound the port, told the connecting client about it, and no connection ever arrived. It is written when the session ends with the listener still waiting, which is normally the moment the connecting client gives up. It says nothing about which firewall, only that the data connection did not reach the CompleteFTP server.
In those two lines 192.168.1.50, the last field of the session tag, is the connecting client's
address. In the block below, the address in Bound to is the CompleteFTP machine's own interface,
which is a different thing and the one item 9 is about.
Only visible at Debug:
2026-08-03 09:13:58,120 DEBUG PassiveListener [Session.41:Default Site.21:ftpuser:192.168.1.50] PASV port range [50000,50099]
2026-08-03 09:13:58,121 DEBUG FTPConfiguration [Session.41:Default Site.21:ftpuser:192.168.1.50] Next passive port = 50017
2026-08-03 09:13:58,122 DEBUG PassiveListener [Session.41:Default Site.21:ftpuser:192.168.1.50] Bound to 192.168.1.10 on port 50017
2026-08-03 09:13:58,340 DEBUG PassiveListener [Session.41:Default Site.21:ftpuser:192.168.1.50] OnConnected
Raising the level takes effect on apply, without restarting the service, and it is not retroactive:
the lines only exist for connections made after the change. A PASV port range line reading
[0,0] means no range is configured. An OnConnected line means the data connection did arrive,
which moves the investigation past everything in Case A steps 1 and 2. A transfer answered at once
with 425 Can't open data connection. is a different failure again: the data socket could not be
created, and the reason is written to Diagnostics.log at Error level.
The 20 seconds in Connection timed out after 20 seconds of inactivity is the connecting client's
own timeout, not a CompleteFTP setting. Left unconfigured, CompleteFTP waits indefinitely for the
data connection, so no server-side timeout needs adjusting to make a slow client work.
Related
- Product guide: How to use CompleteFTP with firewalls, How to set a PASV port range, How to set an external IP address, Active and Passive Modes, FTP/FTPS settings, Logging.
- KB: Turning on debug logging in CompleteFTP for the level change and the log locations, IP filtering and whitelisting in CompleteFTP for the filter rules that also apply to data connections.
- Background on which ports each protocol needs and why the passive range exists: SFTP and FTPS ports explained.
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
- 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 SFTP transfers fail with "ConsumeWindowSpace timed out", and how to fix it
- Why an SSL certificate won't import into CompleteFTP, and how to fix it