dotnet / dotnet/SqlClient

Improve the timeout error when closing an incompletely consumed SqlDataReader

Open
#4,639 0 comments 0 reactions 0 assignees View on GitHub
:bug: Bug! Area\Managed SNI
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.