files-community/Files · error · IOException

Couldn't generate unique name. File skipped.

Error message

Couldn't generate unique name. File skipped.

What it means

Thrown by FtpStorageFolder.CreateFileAsync when UploadStream returns FtpStatus.Skipped. With overwrite=false the upload uses FtpRemoteExists.Skip, so any pre-existing file at the target path causes the server to skip the upload and FluentFTP reports Skipped. The comment indicates this path was meant to honor a GenerateUniqueName collision option that this overload does not actually implement.

Source

Thrown at src/Files.App.Storage/Ftp/FtpStorageFolder.cs:147

			using var ftpClient = GetFtpClient();
			await ftpClient.EnsureConnectedAsync(cancellationToken);

			var newPath = $"{Id}/{desiredName}";
			if (overwrite && await ftpClient.FileExists(newPath, cancellationToken))
				throw new IOException("File already exists.");

			using var stream = new MemoryStream();
			var result = await ftpClient.UploadStream(stream, newPath, overwrite ? FtpRemoteExists.Overwrite : FtpRemoteExists.Skip, token: cancellationToken);

			if (result == FtpStatus.Success)
			{
				// Success
				return new FtpStorageFile(newPath, desiredName, this);
			}
			else if (result == FtpStatus.Skipped)
			{
				// Throw exception since flag CreationCollisionOption.GenerateUniqueName was not satisfied
				throw new IOException("Couldn't generate unique name. File skipped.");
			}
			else
			{
				// File creation failed
				throw new IOException("File creation failed.");
			}
		}

		/// <inheritdoc/>
		public async Task<IChildFolder> CreateFolderAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default)
		{
			using var ftpClient = GetFtpClient();
			await ftpClient.EnsureConnectedAsync(cancellationToken);

			var newPath = $"{Id}/{desiredName}";
			if (overwrite && await ftpClient.DirectoryExists(newPath, cancellationToken))
				throw new IOException("Directory already exists.");

View on GitHub (pinned to 68c68a58d4)

Solutions

  1. Pass overwrite=true if replacing the existing file is acceptable.
  2. Generate a unique name at the call site before invoking CreateFileAsync (the overload here does not implement GenerateUniqueName despite the comment).
  3. Pre-check with ftpClient.FileExists and either rename or delete the conflicting file first.
  4. Fix the inverted overwrite guard (see error 1) so the existence conflict is reported at a deterministic point.

Example fix

// before
var file = await folder.CreateFileAsync(name, overwrite: false, ct);

// after
var attempt = 0;
string unique = name;
while (await FtpExists(folder, unique))
    unique = $"{Path.GetFileNameWithoutExtension(name)} ({++attempt}){Path.GetExtension(name)}";
var file = await folder.CreateFileAsync(unique, overwrite: false, ct);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure a unique name before creating when overwrite is false.
string name = desiredName;
int i = 1;
while (!overwrite && await FtpFileExists(folder, name))
    name = $"{Path.GetFileNameWithoutExtension(desiredName)} ({i++}){Path.GetExtension(desiredName)}";
await folder.CreateFileAsync(name, overwrite, ct);

Try / catch

try { return await folder.CreateFileAsync(name, false, ct); }
catch (IOException ex) when (ex.Message.Contains("skipped", StringComparison.OrdinalIgnoreCase))
{ /* generate a unique name and retry */ }

Prevention

When it happens

Trigger: CreateFileAsync with overwrite=false (default) when {Id}/{desiredName} already exists on the FTP server; the inverted overwrite guard at line 133 does not catch it, so the skip surfaces here.

Common situations: Pasting a file into a folder that already contains a file of the same name with the default 'fail if exists' collision option; uploads after a partial/interrupted prior upload left a zero-byte stub.

Related errors


AI-assisted analysis of files-community/Files@68c68a58d4 (2026-08-13). Data as JSON: /api/errors/6190f9a494822f8c. Report an issue: GitHub.