Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/core/compatibility/11.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ See [Breaking changes in ASP.NET Core 10](/aspnet/core/breaking-changes/10/overv
| [Environment.TickCount made consistent with Windows timeout behavior](core-libraries/11/environment-tickcount-windows-behavior.md) | Behavioral change |
| [MemoryStream maximum capacity updated and exception behavior changed](core-libraries/11/memorystream-max-capacity.md) | Behavioral change |
| [API obsoletions with non-default diagnostic IDs (.NET 11)](core-libraries/11/obsolete-apis.md) | Source incompatible |
| [SafeFileHandle.IsAsync and FileStream.IsAsync accurately reflect non-blocking state on Unix](core-libraries/11/safefilehandle-isasync-unix.md) | Behavioral change |
| [TAR-reading APIs verify header checksums when reading](core-libraries/11/tar-checksum-validation.md) | Behavioral change |
| [ZipArchive.CreateAsync eagerly loads ZIP archive entries](core-libraries/11/ziparchive-createasync-eager-load.md) | Behavioral change |

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
title: "Breaking change: SafeFileHandle.IsAsync and FileStream.IsAsync accurately reflect non-blocking state on Unix"
description: "Learn about the breaking change in .NET 11 where SafeFileHandle.IsAsync and FileStream.IsAsync on Unix now accurately reflect whether the underlying file descriptor is in non-blocking mode."
ms.date: 04/07/2026
ai-usage: ai-assisted
---

# SafeFileHandle.IsAsync and FileStream.IsAsync accurately reflect non-blocking state on Unix

On Unix, <xref:Microsoft.Win32.SafeHandles.SafeFileHandle.IsAsync?displayProperty=nameWithType> and <xref:System.IO.FileStream.IsAsync?displayProperty=nameWithType> now accurately reflect whether the underlying file descriptor has the `O_NONBLOCK` flag set. Previously, `IsAsync` unconditionally returned `true` for regular files opened with <xref:System.IO.FileOptions.Asynchronous?displayProperty=nameWithType>, even though regular file I/O on Unix is inherently synchronous at the kernel level.

## Version introduced

.NET 11 Preview 3

## Previous behavior

Previously, `SafeFileHandle.IsAsync` always returned `true` on Unix for regular files opened with `FileOptions.Asynchronous`, regardless of whether the underlying file descriptor had the `O_NONBLOCK` flag set.

```csharp
using Microsoft.Win32.SafeHandles;

// On Unix, IsAsync was true for regular files opened with FileOptions.Asynchronous,
// even though the file descriptor had no O_NONBLOCK set.
using SafeFileHandle handle = File.OpenHandle("myfile.txt", options: FileOptions.Asynchronous);
Console.WriteLine(handle.IsAsync); // true (misleading; no O_NONBLOCK on regular file)
```

On non-Windows platforms, constructing a `SendPacketsElement` with a non-async `FileStream` threw an `ArgumentException`.

Comment thread
gewarren marked this conversation as resolved.
## New behavior

Starting in .NET 11, `SafeFileHandle.IsAsync` on Unix returns `true` only when the underlying file descriptor has the `O_NONBLOCK` flag set, which is possible for pipes and sockets. For regular files, it returns `false`.

```csharp
using Microsoft.Win32.SafeHandles;

// On Unix, IsAsync now reflects the actual
// non-blocking state of the file descriptor.
SafeFileHandle.CreateAnonymousPipe(
out SafeFileHandle readHandle,
out SafeFileHandle writeHandle,
asyncRead: true,
asyncWrite: false);

Console.WriteLine(readHandle.IsAsync); // true (O_NONBLOCK set on read end)
Console.WriteLine(writeHandle.IsAsync); // false (blocking write end)
```

For regular files opened with `FileOptions.Asynchronous`, `IsAsync` correctly returns `false` on Unix because regular file I/O is inherently synchronous at the kernel level.

Additionally, on non-Windows platforms, constructing a `SendPacketsElement` with a `FileStream` no longer throws `ArgumentException` regardless of whether the stream is async.

## Type of breaking change

This change is a [behavioral change](../../categories.md#behavioral-change).

## Reason for change

The previous behavior was incorrect and misleading. `SafeFileHandle.IsAsync` reported `false` for file descriptors (such as pipes or sockets) that genuinely had `O_NONBLOCK` set. This caused APIs and user code that relied on this property to make incorrect decisions. Accurate `IsAsync` reporting was also a prerequisite for the new `SafeFileHandle.CreateAnonymousPipe` API to correctly expose per-end async semantics on Unix. For more information, see [dotnet/runtime#125220](https://github.com/dotnet/runtime/pull/125220).

## Recommended action

Review any code that checks `SafeFileHandle.IsAsync` or `FileStream.IsAsync` on Unix and takes action based on the result:

- If your code assumed `IsAsync` was always `true` on Unix for files opened with `FileOptions.Asynchronous`, update it to account for the fact that regular file handles now return `false`.

- If you wrap a non-blocking `SafeFileHandle` in a `FileStream` (for example, one created via `SafeFileHandle.CreateAnonymousPipe` with `asyncRead: true` or `asyncWrite: true`), `FileStream.IsAsync` might now return `true` where it previously returned `false`. Adjust downstream code accordingly.

- If you construct `SendPacketsElement` with a `FileStream` on a non-Windows platform and previously expected an `ArgumentException` to be thrown for non-async streams, note that the exception is no longer thrown. Guard any such expectation with `OperatingSystem.IsWindows()`.

## Affected APIs

- <xref:Microsoft.Win32.SafeHandles.SafeFileHandle.IsAsync?displayProperty=fullName>
- <xref:System.IO.FileStream.IsAsync?displayProperty=fullName>
- <xref:System.Net.Sockets.SendPacketsElement.%23ctor(System.IO.FileStream,System.Int64,System.Int32)?displayProperty=fullName>
- <xref:System.Net.Sockets.SendPacketsElement.%23ctor(System.IO.FileStream,System.Int64,System.Int32,System.Boolean)?displayProperty=fullName>
2 changes: 2 additions & 0 deletions docs/core/compatibility/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ items:
href: core-libraries/11/memorystream-max-capacity.md
- name: API obsoletions with non-default diagnostic IDs
href: core-libraries/11/obsolete-apis.md
- name: SafeFileHandle.IsAsync and FileStream.IsAsync accurately reflect non-blocking state on Unix
href: core-libraries/11/safefilehandle-isasync-unix.md
- name: TAR-reading APIs verify header checksums when reading
href: core-libraries/11/tar-checksum-validation.md
- name: ZipArchive.CreateAsync eagerly loads ZIP archive entries
Expand Down
Loading