Improve the timeout error when closing an incompletely consumed SqlDataReader
- Dominant language
- C#
- Stars
- 989
- Forks
- 340
- Avg merge
- 4d 19h
- Merged PRs (30d)
- 72
Description
### Describe the bug
Closing a `SqlDataReader` before consuming the full result set drains the remaining rows while the command timeout is still armed. If the drain exceeds the timeout on Linux, the caller receives an opaque timeout ending in `Win32Exception (258): Unknown error 258`.
The behavior is expected, but the error does not explain that the timeout occurred while draining an incompletely consumed result set or suggest canceling the command before closing the reader.
This is Cause B from #1530. See the [deterministic analysis and version matrix](https://github.com/dotnet/SqlClient/issues/1530#issuecomment-5404606795) and the [request to track an improved error separately](https://github.com/dotnet/SqlClient/issues/1530#issuecomment-5528129710).
```
Exception message: Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
Inner exception: System.ComponentModel.Win32Exception (258): Unknown error 258
Stack trace: The exception is raised from SqlDataReader.Close/CloseAsync while draining the remaining rows.
```
### To reproduce
1. Execute a query that returns enough rows that draining them exceeds a short `CommandTimeout`.
2. Read only the first row.
3. Call `SqlDataReader.Close()` or `CloseAsync()` without first canceling the command.
4. Observe the timeout with `Unknown error 258`.
The linked analysis includes results across Microsoft.Data.SqlClient 5.1.5 through 7.1.0-preview2.
### Expected behavior
Surface an error that identifies the timeout as occurring while closing and draining an incompletely consumed reader, with guidance to consume the result set or cancel the command before closing it.
### Further technical details
Microsoft.Data.SqlClient version: Reproduced from 5.1.5 through 7.1.0-preview2
.NET target: net8.0 and net9.0 assets, tested on .NET 8, .NET 9, and .NET 10
SQL Server version: Reproduced with SQL Server in Docker
Operating system: Linux
**Additional context**
Explicit `Close()` and `CloseAsync()` surface the exception. `Dispose()` currently swallows the `SqlException` from `Close()`. EF Core calls the throwing close paths, while Dapper cancels the command during teardown and avoids this result.
Contributor guide
Research direction
Start at SqlDataReader.Close/CloseAsync and reproduce with a short CommandTimeout after reading only the first row, comparing the Linux timeout and inner exception. Trace the draining path and its interaction with cancellation and Dispose; done means the surfaced error identifies closing and draining an incompletely consumed reader and advises consuming the result set or canceling before close.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, sql
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100