Skip to content

Configurable SftpFileStream.MaxPendingReads #1819

Open

Description

@GMMan

Is it possible to make SftpFileStream.MaxPendingReads a configurable value? I'm currently dealing with a SFTP implementation by a third party integration that applies some special behavior when the file has been read, and it seems like it might be doing so when the read hits EOF. Since this library spams read requests at the server when the file is being downloaded and the size is known, I'm suspecting the server is applying the behavior early due to a race condition, since there's no requirement in the protocol to process requests in the same order as received (actually there is for the same file, but it also mentions something about parallelization that makes this slightly ambiguous) or reads to occur linearly. I don't think the third party will be able to change their implementation soon, so second best thing would be to configure it in this library so there's only one concurrent request, ideally without having to fork and maintain my own copy.

Activity

  1. mus65 commented on Aug 10, 2026

    @mus65
    Contributor

    There already is an open PR for this: #1796

  2. Rob-Hague commented on Aug 11, 2026

    @Rob-Hague
    Collaborator

    I would be fine with it but I would like to see some evidence that it is actually the problem. OpenSSH client also sends lots of requests in parallel, and I don't personally see any ambiguity in the spec (I am looking at https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02#section-6.1)

  3. GMMan commented on Aug 11, 2026

    @GMMan
    Author

    I don't think it has any conflicts with the spec, but sometimes real world applications don't behave well and it would be useful to have some control over parameters that might help alleviate symptoms. Said third-party replied to me today that they're using a vendor-provided implementation that should be protocol compliant, and they see it working fine with most clients. (Basically they move files to an archive folder after they've been read, and most of the time I'm seeing the last chunk missing, so my guess is they are applying the action on EOF read and an out of order read request would cause this.)

    #1795 is another issue that demonstrates adjusting the parameter helps in their case.

  4. mus65 commented on Aug 22, 2026

    @mus65
    Contributor

    The only behaviour difference I see is that OpenSSH seems to always start with 1 request:

    https://github.com/openssh/openssh-portable/blob/0ef0f5a839831c213f24e3f2ae434765c607fb50/sftp-client.c#L1666

    SSH.NET on the other hand may start with a lot, depending on file size since #1705 .

    var initialPendingReads = (int)Math.Max(1, Math.Min(MaxPendingReads, 2 + (attributes.Size / readBufferSize)));

    Also, OpenSSH maxes out at 64 (DEFAULT_NUM_REQUESTS) by default instead of 100 .

    Not saying SSH.NET has a bug, but maybe that explains why only SSH.NET triggers it.

    @GMMan Can you also reproduce it if you use OpenAsync and copy the stream manually instead of using DownloadAsync? That would skip the the mentioned initialPendingReads calculation and start with 1 like OpenSSH does.

  5. karlanke commented on Sep 17, 2026

    @karlanke

    Note that I'm also seeing what feels like this issue - moving from v2024.x to v2026.0.0 and downloads stall until the server closes the connection. I am, however, using OpenAsync

  6. GMMan commented on Oct 20, 2026

    @GMMan
    Author

    @GMMan Can you also reproduce it if you use OpenAsync and copy the stream manually instead of using DownloadAsync? That would skip the the mentioned initialPendingReads calculation and start with 1 like OpenSSH does.

    Got this applied in a recent deployment + updated to latest NuGet package version, and from a few days of monitoring I'm no longer seeing the original issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions