Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

SFTP CreateDirectoryAsync is not idempotent

Abierto
#1,747 4 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
38/100
Tipo de issue
Nueva funcionalidad
Claridad
Bastante claro
Estado de actividad
Tranquilo
Stack tecnológico
csharp
Área
networking

Línea de trabajo

Empieza por SftpClient.CreateDirectoryAsync y su documentación XML existente; después, compara su comportamiento con ExistsAsync y con la respuesta SFTP reportada cuando el directorio ya existe. Determina si el comportamiento idempotente solicitado o una excepción más específica encaja con el protocolo; se considera terminado cuando los llamadores pueden distinguir un directorio existente de otros errores.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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?

Lenguaje dominante
C#
Estrellas
4.4k
Forks
993
Merge medio
9 d 21 h
PR fusionados (30 d)
1

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de sshnet/SSH.NET

Todos los issues de sshnet/SSH.NET

Issues similares

Más issues de C#

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.