dotnet / dotnet/sdk

.NET10 FtpWebRequest - Breaking Change - EnableSsl=true leads to encoded output

Open
#52,324 0 comments 2 reactions 0 assignees View on GitHub
untriaged
Dominant language
C#
Stars
3.2k
Forks
1.3k
PR merge metrics
PR metrics pending

Description

### Describe the bug
In .NET 10, `FtpWebRequest` appears to have a breaking behavioral change when using
`WebRequestMethods.Ftp.ListDirectoryDetails` with `EnableSsl = true`.

This change is **not documented in the .NET 10 breaking changes list** and appears to be a regression.

**Observed behavior:**
- In .NET 8 / .NET 9:
- Listing an empty FTP directory returns an empty response (`string.Empty`)
- In .NET 10:
- The same call returns **non-empty binary/garbled data** even when the directory is empty
- Disabling SSL (`EnableSsl = false`) restores the old behavior, but is not an acceptable workaround due to credential exposure.

This breaks existing logic that checks directory existence or contents based on an empty response.

### To Reproduce
I created a test project with the following code and connected to my ftp server (internal use only, so i will not share that or credentials)
```csharp
public class Program {
static void Main(string[] args) {
if (args.Length != 3) {
Console.WriteLine("Usage: FtpTestNet10 \"\" ");
return;
}

string ftpServerUrl = args[0];
string username = args[1];
string password = args[2];
NetworkCredential networkCredential = new NetworkCredential(username, password);

CheckDirectory(ftpServerUrl, networkCredential);
}

/// Creates a new directory in the rebuild directory of the ftp server.
/// The name of the directory which will be created.
/// True if the created worked.
private static void CheckDirectory(string uploadFolderPath, NetworkCredential networkCredential) {
try {
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(uploadFolderPath); // check if the given path already exists
request.Credentials = networkCredential;
request.EnableSsl = true;
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails; // get content of the upload folder.
string? responseContent = null;
using (var response = request.GetResponse()) {
using (var responseStream = response.GetResponseStream()) {
using (StreamReader reader = new StreamReader(responseStream)) {
responseContent = reader.ReadToEnd();
}
}
}
if (responseContent == null) {
responseContent = "--null--";
}
if (responseContent == string.Empty) {
responseContent = "--empty--";
}
Console.WriteLine($"Response: {responseContent}");
}
catch (WebException ex) {
Console.WriteLine("Error: " + ex.Message);
}
}
}
```
Example console output:

- **net9.0**
"Response: --empty--"
- **net10.0**
"Response: ␦??details of dotnet --info

.NET SDK:
Version: 10.0.101
Commit: fad253f51b
Workload version: 10.0.101
MSBuild version: 18.0.6+fad253f51

Runtime Environment:
OS Name: Windows
OS Version: 10.0.26200
OS Platform: Windows
RID: win-x64
Base Path: C:\Program Files\dotnet\sdk\10.0.101\

.NET workloads installed:
[android]
Installation Source: SDK 10.0.100, VS 18.1.11312.151, VS 17.14.36717.8
Manifest Version: 36.1.2/10.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.android\36.1.2\WorkloadManifest.json
Install Type: Msi

[ios]
Installation Source: SDK 10.0.100, VS 18.1.11312.151, VS 17.14.36717.8
Manifest Version: 26.1.10502/10.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.ios\26.1.10502\WorkloadManifest.json
Install Type: Msi

[maccatalyst]
Installation Source: SDK 10.0.100, VS 18.1.11312.151, VS 17.14.36717.8
Manifest Version: 26.1.10502/10.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.maccatalyst\26.1.10502\WorkloadManifest.json
Install Type: Msi

[maui-windows]
Installation Source: SDK 10.0.100, VS 18.1.11312.151, VS 17.14.36717.8
Manifest Version: 10.0.1/10.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.sdk.maui\10.0.1\WorkloadManifest.json
Install Type: Msi

[wasm-tools]
Installation Source: SDK 10.0.100
Manifest Version: 10.0.101/10.0.100
Manifest Path: C:\Program Files\dotnet\sdk-manifests\10.0.100\microsoft.net.workload.mono.toolchain.current\10.0.101\WorkloadManifest.json
Install Type: Msi

Configured to use workload sets when installing new manifests.

Host:
Version: 10.0.1
Architecture: x64
Commit: fad253f51b

.NET SDKs installed:
9.0.308 [C:\Program Files\dotnet\sdk]
10.0.101 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.22 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 9.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 10.0.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.22 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 9.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 10.0.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 6.0.36 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 8.0.22 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 9.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 10.0.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
x86 [C:\Program Files (x86)\dotnet]
registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
Not set

global.json file:
Not found

Learn more:
https://aka.ms/dotnet/info

Download .NET:
https://aka.ms/dotnet/download

- The IDE Visual Studio 2022 and 2026

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.