sshnet / sshnet/SSH.NET

Improve exception from SftpClient.BeginDownloadFile

Open
#498 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
4.4k
Forks
993
Avg merge
9d 21h
Merged PRs (30d)
1

Description

I recently ran into an issue using SftpClient.BeginDownloadFile(). It turned out that the asyncCallback function I was passing was throwing an exception, but it was more difficult than anticipated to find the root cause because the exception from my function was caught by BeginDownloadFile(), which then reacts by calling asyncResult.SetAsCompleted(exp, false), which in turn throws an InvalidOperationException because the completion flag was already set before executing my callback function. (The root cause becomes apparent when breaking on first-chance exceptions but I often have that turned off due to nuisances it can create.)

As I see it, a simple thing to do to improve this would be to modify AsyncResult.SetAsCompleted() to set the inner exception (it's already passed the exception causing the completion):

throw new InvalidOperationException("You can set a result only once");

changes to

throw new InvalidOperationException("You can set a result only once", exception);

I don't know what other contexts this may affect, but it seems reasonable....

Another possibility is to break up the try/catch in SftpClient.BeginFileDownload() so asyncResult.SetAsCompleted(exp, false) gets called only if InternalDownloadFile() throws and asyncResult.SetAsCompleted(null, false) gets called otherwise. Something like

bool success = false;
try
{
    InternalDownloadFile(path, output, asyncResult, offset =>
    {
        asyncResult.Update(offset);
        if (downloadCallback != null)
        {
            downloadCallback(offset);
        }
    });
    success = true;
}
catch (Exception exp)
{
    asyncResult.SetAsCompleted(exp, false);
}
if (success)
{
    asyncResult.SetAsCompleted(null, false);
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with SftpClient.BeginDownloadFile(), InternalDownloadFile(), and AsyncResult.SetAsCompleted() to trace how callback exceptions are caught and completion is marked. Compare the proposed exception-preservation and control-flow approaches; done means the original callback failure remains discoverable instead of being obscured by a second InvalidOperationException.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
networking
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.