files-community/Files · error · IOException

File already exists.

Error message

File already exists.

What it means

Thrown by FtpStorageFolder.CreateFileAsync when the target file already exists. NOTE: the guard condition appears INVERTED. The code reads `if (overwrite && await ftpClient.FileExists(...))`, so it throws precisely when overwrite is true and the file exists - the opposite of the intended overwrite semantics. With the default overwrite=false, an existing file is NOT caught here and instead falls through to produce a different error via the Skipped result.

Source

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

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

			var newItem = await CreateCopyOfAsync(itemToMove, overwrite, cancellationToken);
			await source.DeleteAsync(itemToMove, cancellationToken);

			return newItem;
		}

		/// <inheritdoc/>
		public async Task<IChildFile> CreateFileAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default)
		{
			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.");

View on GitHub (pinned to 68c68a58d4)

Solutions

  1. Fix the inverted condition in FtpStorageFolder.cs:133 to `if (!overwrite && await ftpClient.FileExists(newPath, cancellationToken))` so existence is only fatal when overwrite is not requested.
  2. Before calling CreateFileAsync, check existence and choose overwrite/unique-name handling at the call site.
  3. Catch IOException with this message and retry with overwrite=true only if overwriting is actually desired.

Example fix

// before (FtpStorageFolder.cs:133)
if (overwrite && await ftpClient.FileExists(newPath, cancellationToken))
    throw new IOException("File already exists.");

// after
if (!overwrite && await ftpClient.FileExists(newPath, cancellationToken))
    throw new IOException("File already exists.");
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check existence before creating.
var target = $"{ftpFolder.Id}/{desiredName}";
if (await ftpClient.FileExists(target, ct))
{
    if (!overwrite) throw new IOException($"{desiredName} already exists.");
    // else proceed with overwrite
}

Try / catch

try { await folder.CreateFileAsync(name, overwrite, ct); }
catch (IOException ex) when (ex.Message == "File already exists.")
{ /* retry with overwrite=true or a unique name */ }

Prevention

When it happens

Trigger: Calling CreateFileAsync(name, overwrite: true) on an FtpStorageFolder when a file with that name already exists at {Id}/{desiredName}. Transiently hit through MoveFromAsync -> CreateCopyOfAsync -> CreateFileAsync with overwrite propagated.

Common situations: Copy/paste of a file whose name already exists at the destination; automated sync tools that retry with overwrite=true; test fixtures that leave files behind between runs.

Related errors


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