Summary

  • FTP divided one file action between a control connection and a data connection. A 1yz reply announced that work had begun but remained incomplete; a later 2yz, 4yz or 5yz reply supplied the command outcome.
  • Closure of the data connection was deliberately ambiguous. It could follow normal end of file, an abort, a changed port instruction, loss of the control connection or an irrecoverable error.
  • During an active ABOR, 426 belongs to the original transfer and the following 226 belongs to the abort command. Reading only the last positive code turns a successfully processed cancellation into a successful transfer.

The last byte was not the verdict

Picture a client receiving a large file over FTP. The progress counter reaches the expected size. TCP reports an orderly close on the data connection. A file with the expected name exists. A dashboard can easily compress those observations into a green word: complete.

FTP withheld that conclusion. The file bytes travelled on one connection, while commands and numbered replies travelled on another. The data path could show that no more octets were arriving. It could not, by itself, say why they stopped or which requested action the server believed it had completed.

That division created an unusual evidence problem. One user action had two timelines. The data-transfer process observed connection establishment, octets, end of file and transport failure. The protocol interpreter observed a command, a preliminary reply and a terminal reply. Neither record was sufficient when separated from the other.

The design was not an accidental consequence of TCP. FTP had deliberately made its control conversation persistent and its data connections disposable. A listing, a file or a series of files could use a temporary data path while the command session stayed alive. The price of that flexibility was a rule that modern automation still often forgets: a transport event cannot silently inherit the meaning of an application result.

Early FTP already separated completion from closure

The distinction predates the familiar three-digit reply families. In July 1972, RFC 354 listed 252 FTP transfer completed correctly. It separately listed 452 FTP: File transfer incomplete, data connection closed.

Both outcomes could include a closed data connection. The difference lay in the server's account of the file action. That early vocabulary is a compact historical warning against treating a socket close as a success receipt.

FTP's architecture also reflected the machines it connected. Control information used a Telnet dialogue. Data could require a different representation, structure or transfer mode. A server protocol interpreter coordinated local filesystem work with a server data-transfer process; the client had its own corresponding pair. The control connection therefore carried the shared state that the data path could not express.

The reply scheme still needed improvement. Human-readable sentences varied among servers, and early replies could be difficult for programs to classify. A client needed to know not merely that a line sounded encouraging, but whether it could issue another command, wait for data, supply more information or retry later.

The first digit became a state transition

RFC 640, published in 1974 as a reply-code revision to the then-current FTP specification, organized the first digit around command state.

A 1yz response was positive preliminary. The requested action had started, but the reply explicitly left it incomplete. Another reply would follow. A 2yz response was positive completion: the requested action had completed and a new request could begin. A 3yz response was positive intermediate and waited for more information. A 4yz response was transient negative completion, while 5yz was permanent negative completion for the request as issued.

That classification prevented “positive” from becoming one undifferentiated bucket. 150 could authorize a client to attend to the data connection without claiming that the file action had succeeded. 226 could close the state transition. A failure code could instead terminate it.

RFC 640 also moved preliminary timing earlier. The server could send the preliminary reply once transfer was possible and the data connection existed or was about to be attempted; it did not have to wait for the connection to be fully established. This made the dialogue faster and more useful, but it narrowed what the preliminary response proved. It announced readiness and attempted progression, not delivery.

One command expected a second reply

RFC 765 and the canonical 1985 RFC 959 retained the two-stage logic. RFC 959 states that a command can receive at most one 1yz reply. After that preliminary response, the server must eventually send a completion reply.

For file actions such as RETR, STOR or STOU, the command tables show the shape. A 125 means the data connection is already open and transfer is starting. A 150 says the file status is okay and the server is about to open the data connection. Neither is terminal. The later outcomes include 226 or 250 for a successful file action, or applicable failures such as 425, 426, 451, 551 and 552.

The exact code matters, but so does its position. A trace containing 150 and many bytes is an unfinished trace. A data close without the final control reply is an unknown command outcome at the FTP layer. A terminal failure does not become success because a byte counter happened to match an expectation.

The separation also explains why clients normally wait for the completion reply before beginning the next ordinary command. The control interpreter is still inside the previous state transition. ABOR and STAT need special handling precisely because they may have to interrupt or inspect work that is still active.

The data connection had several legitimate endings

RFC 959 lists multiple reasons for the server to close a data connection. It may close normally after a transfer. It may receive an abort request. The user may issue a command that changes the port specification. The control connection may close legally or unexpectedly. An irrecoverable error may occur.

Those causes share a transport symptom but not a protocol meaning. Even an orderly TCP close only describes the transport exchange. It does not identify whether the file action ended at the intended boundary, whether the server committed a local side effect, or whether an abort deliberately cut the stream short.

Transfer mode makes the distinction sharper. In Stream mode, closing the data connection can serve as end of file. That is a framing fact. It still is not a complete account of the command. RFC 959 itself notes the problem around restart: closing the connection leaves no way to distinguish a normal end from an abnormal one without the surrounding control evidence.

A correctly designed observer therefore keeps two event types. “Data EOF or close observed” is one. “FTP action completed with code X” is another. They may occur close together, but one must not overwrite the other.

A positive 226 could complete a cancellation

The ABOR sequence exposes the danger of reducing a conversation to its final line.

If the requested service has already finished, the abort command has nothing left to stop. The server nevertheless sends 226 to report that it successfully processed ABOR. That reply is about the abort request, not a newly issued warranty for the earlier file.

If the transfer is still active, RFC 959 prescribes two replies. First, 426 reports that the data connection closed and that the original transfer was aborted. Then 226 reports successful processing of the abort command.

The order contains two results for two actions:

  1. original transfer → abnormal termination (426);
  2. abort request → successful completion (226).

A parser that retains only the last numeric code sees a positive completion and may label the transfer successful. A parser that associates both replies with the original command may call the abort itself a failure. Both errors erase the action boundary.

This is why an ordered transcript matters more than a bag of response codes. Meaning comes from command ownership, reply class and sequence. The same 226 family can accompany successful transfer closure or successful abort processing. The preceding state determines which claim is justified.

226 stopped at the server's protocol boundary

Even an unambiguous transfer 226 is a bounded statement. RFC 959 describes the requested file action as successful and the data connection as closing. It does not attach a content digest. It does not name a version of the remote object. It does not promise that bytes reached durable media, survived a later filesystem fault, entered a backup, became readable to another principal or were consumed by an application.

Those stronger claims require separate evidence. A digest can compare content. A server-specific commit or fsync record can speak to local persistence. An immutable object identifier can distinguish versions. An application acknowledgement can report downstream acceptance. A commercial workflow can decide whether a transfer satisfies an order or filing obligation.

Nor does the absence of 226 prove that no side effect occurred. The server might complete a write and lose the control connection before the client receives the reply. Retrying blindly can create a duplicate or overwrite. The protocol trace has become uncertain; it has not made the remote filesystem run backward.

FTP's reply discipline thus offers neither pessimism nor optimism. It offers attribution. A client knows which claim the protocol made, which claim it did not make and which evidence disappeared when one channel failed.

The control transcript was part of the transfer record

Operational logging often privileges bulk data metrics: bytes, duration, checksum, TCP flags and filenames. FTP requires the control transcript to sit beside them. At minimum, an auditable record needs the command and its order, the preliminary reply, the selected data endpoint, establishment outcome, byte and EOF observations, close cause when known, every terminal reply, and the timing of ABOR or STAT.

The record should also preserve facts that FTP does not certify: local pathname resolution, representation and transfer mode, file-action side effects, persistence results and any independent digest or version check. These are not redundant. They answer different questions.

Several states deserve explicit names rather than a Boolean done: preliminary accepted; data connection pending; data active; data closed awaiting final reply; transfer positively completed; transfer negatively completed; abort requested; original transfer aborted; abort processed; control outcome unknown.

Once those states are collapsed, later reconstruction becomes impossible. A dashboard can no longer distinguish a clean success from a successful cancellation, or a remote failure from a lost receipt. The irreversible damage is not the socket close. It is the deletion of the sequence that explained it.

Sources and evidence boundary

The historical account is grounded in RFC 354, RFC 542, RFC 640, RFC 765, RFC 959 and RFC 1123. They establish the reply classes, the separate connections, the completion sequences, the close reasons and the historical ABOR requirements.

RFC 1123 observed in 1989 that restart, ABOR and Block mode were useful robustness features but were not widely implemented. That sentence is historical, not a measurement of present software. The source set does not establish current product behavior, conformance, prevalence or filesystem durability. The article therefore describes protocol authority and evidence boundaries, not a census of today's FTP deployments.