Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

SFTP CreateDirectoryAsync is not idempotent

Aperta
#1,747 4 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
38/100
Tipo di issue
Funzionalità
Chiarezza
Abbastanza chiara
Stato di attività
Tranquilla
Stack tecnologico
csharp
Ambito
networking

Direzione di ricerca

Parti da SftpClient.CreateDirectoryAsync e dalla relativa documentazione XML esistente, quindi confrontane il comportamento con ExistsAsync e con la risposta SFTP riportata quando la directory esiste già. Determina se il comportamento idempotente richiesto o un'eccezione più specifica è compatibile con il protocollo; il lavoro è completato quando i chiamanti possono distinguere una directory esistente dagli altri errori.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

My specifications:
OS: Ubuntu Desktop and Windows 11
Framework: .NET 9
Package version: 2025.1.0

This is my current code:

using SftpClient client = new SftpClient(host, port, username, password);
await client.ConnectAsync(cancellationToken);
try
{
        await client.CreateDirectoryAsync(someDir, cancellationToken);
}
catch (SftpException ex)
{
        // Only some logging code here.
}

When this directory already exists, all I get as an exception message is "failure".

Renci.SshNet.Common.SftpException: failure
 at Renci.SshNet.SubsystemSession.<WaitOnHandleAsync> g__DoWaitAsync | 38_0[T](TaskCompletionSource`1 tcs, Int32 millisecondsTimeout, CancellationToken cancellationToken)
 at Renci.SshNet.SftpClient.CreateDirectoryAsync(String path, CancellationToken cancellationToken)

Problems:

  • I can't know for sure if the exception is about the directory already existing, or whether something else is wrong. Type SftpException with message "failure" is just not descriptive enough.
  • CreateDirectoryAsync is not idempotent. Throwing an exception seems very counter-intuitive for a method like this.
  • The SftpException is not described in the method documentation:
        /// <summary>
        /// Asynchronously requests to create a remote directory specified by path.
        /// </summary>
        /// <param name="path">Directory path to create.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
        /// <returns>A <see cref="Task"/> that represents the asynchronous create directory operation.</returns>
        /// <exception cref="ArgumentException"><paramref name="path"/> is <see langword="null"/> or contains only whitespace characters.</exception>
        /// <exception cref="SshConnectionException">Client is not connected.</exception>
        /// <exception cref="SftpPermissionDeniedException">Permission to create the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
        /// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>

If I would want to do this properly, I need quite a lot of code. This is the extension method I wrote:

internal static async ValueTask EnsureDirectoryExists(this SftpClient client, string directory, CancellationToken cancellationToken)
{
    bool directoryExists = await client.ExistsAsync(directory, cancellationToken);
    if (directoryExists) return;

    try
    {
        await client.CreateDirectoryAsync(directory, cancellationToken);
    }
    catch (SftpException ex)
    {
        try
        {
            bool directoryAlreadyExisted= await client.ExistsAsync(directory, cancellationToken);
            if (directoryAlreadyExisted)
            {
                // The directory already existed, so we can ignore the exception.
                return;
            }
            else
            {
                // The directory does not exist.
                throw;
            }
        }
        catch (SftpException)
        {
            // Failed checking if the directory exists.
            // Throwing the original exception as that illustrates what really is the problem here.
            throw ex;
        }
    }
}

There are 2 ways this could be fixed:

  1. Add an EnsureDirectoryExists method. I'm sure this can be done much closer to the actual SFTP protocol than the extension method I created.
  2. Add an SftpDirectoryAlreadyExistsException that inherits from SftpException.

I'm very curious, is there a reason why something like EnsureDirectoryExists was never added to SSH.NET?

Lingua principale
C#
Stelle
4.4k
Fork
993
Merge medio
9g 21h
PR unite (30g)
1

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di sshnet/SSH.NET

Tutte le issue di sshnet/SSH.NET

Issue simili

Altre issue su C#

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.