Skip to content

Fix CS4007: ReadOnlySpan across await in TrailingHeadersTest#125946

Merged
MichalStrehovsky merged 2 commits intomainfrom
copilot/fix-readonlyspan-await-issue
Mar 23, 2026
Merged

Fix CS4007: ReadOnlySpan across await in TrailingHeadersTest#125946
MichalStrehovsky merged 2 commits intomainfrom
copilot/fix-readonlyspan-await-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 23, 2026

Assert.Equal<byte>(Array.Empty<byte>(), await ...) resolves the ReadOnlySpan<byte> overload, causing CS4007 since the span can't be preserved across the await boundary — breaking functional test builds for System.Net.Http.WinHttpHandler.

Description

  • TrailingHeadersTest.cs: In Http2GetAsync_TrailingHeaders_NoData_EmptyResponseObserved, extract the awaited call into a local variable before Assert.Equal:
// Before (broken):
Assert.Equal<byte>(Array.Empty<byte>(), await response.Content.ReadAsByteArrayAsync());

// After (fixed):
byte[] responseBody = await response.Content.ReadAsByteArrayAsync();
Assert.Equal(Array.Empty<byte>(), responseBody);
Original prompt

Problem

Building the functional tests for System.Net.Http.WinHttpHandler fails with the following error:

src\libraries\System.Net.Http.WinHttpHandler\tests\FunctionalTests\TrailingHeadersTest.cs(220,36): error CS4007: Instance of type 'System.ReadOnlySpan<byte>' cannot be preserved across 'await' or 'yield' boundary.

Root Cause

In src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/TrailingHeadersTest.cs, line 220, inside the Http2GetAsync_TrailingHeaders_NoData_EmptyResponseObserved async test method:

Assert.Equal<byte>(Array.Empty<byte>(), await response.Content.ReadAsByteArrayAsync());

Assert.Equal<byte>(ReadOnlySpan<byte>, ReadOnlySpan<byte>) overload is being resolved here, which means Array.Empty<byte>() is implicitly converted to a ReadOnlySpan<byte>. Since there is an await expression in the same statement (as the second argument), the compiler cannot preserve the ReadOnlySpan<byte> across the await boundary — resulting in CS4007.

Fix

In the Http2GetAsync_TrailingHeaders_NoData_EmptyResponseObserved method, extract the await call to a local variable before the Assert.Equal call, so that no ReadOnlySpan<byte> lives across an await:

// Before (broken):
Assert.Equal<byte>(Array.Empty<byte>(), await response.Content.ReadAsByteArrayAsync());

// After (fixed):
byte[] responseBody = await response.Content.ReadAsByteArrayAsync();
Assert.Equal(Array.Empty<byte>(), responseBody);

The file to change is:
src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/TrailingHeadersTest.cs

The change is in the Http2GetAsync_TrailingHeaders_NoData_EmptyResponseObserved method, around line 220.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: in testing we started hitting following error, fix it: src\libraries\System.Net.Http.WinHttpHandler\tests\FunctionalTests\TrailingHeadersTest.cs(220,36): error CS4007: (NETCORE_ENGINEERING_TELEMETRY=Build) Instance of type 'System.ReadOnlySpan' cannot be preserved across 'await' or 'yield' boundary.

Assistant: The issue is clear. On line 220, Assert.Equal<byte> is being called inside an async method where the first argument is Array.Empty<byte>() — which returns a ReadOnlySpan<byte> — and the second argument is await response.Content.ReadAsByteArrayAsync(). The ReadOnlySpan<byte> cannot be preserved across the await boundary.

The fix is to await the ReadAsByteArrayAsync() call first, store it in a local variable, and then pass it to Assert.Equal.

Assistant: We're waiting for your response to the tool call dialog to continue — please accept or reject the proposed pull request action so I can proceed.

This pull request was created from Copilot chat.


📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.

Fixes #125952

Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7513d91a-dbcc-4acf-86c6-d9110df1c1c0
Copilot AI changed the title [WIP] Fix ReadOnlySpan usage across await boundary in tests Fix CS4007: ReadOnlySpan across await in TrailingHeadersTest Mar 23, 2026
Copilot AI requested a review from MichalStrehovsky March 23, 2026 05:56
@MichalStrehovsky MichalStrehovsky marked this pull request as ready for review March 23, 2026 05:58
Copilot AI review requested due to automatic review settings March 23, 2026 05:58
@MichalStrehovsky
Copy link
Copy Markdown
Member

Cc @agocke, I think this is from the xunit upgrade, not completely sure why we didn't see this in that PR's CI run, but I now see:

src\libraries\System.Net.Http.WinHttpHandler\tests\FunctionalTests\TrailingHeadersTest.cs(220,36): error CS4007: (NETCORE_ENGINEERING_TELEMETRY=Build) Instance of type 'System.ReadOnlySpan<byte>' cannot be preserved across 'await' or 'yield' boundary.

in various CI legs.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a functional test build failure in System.Net.Http.WinHttpHandler caused by overload resolution selecting an Assert.Equal<byte>(ReadOnlySpan<byte>, ReadOnlySpan<byte>) overload in the presence of an await, which triggers CS4007.

Changes:

  • Store await response.Content.ReadAsByteArrayAsync() into a local byte[] before asserting.
  • Update the assertion to compare the empty byte array against the local variable without an await in the same statement.

@MichalStrehovsky
Copy link
Copy Markdown
Member

/azp run runtime-nativeaot-outerloop

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky
Copy link
Copy Markdown
Member

(Triggering a native AOT outerloop, since we very like won't run the relevant legs here either, but naot outerloops for sure hit this.)

@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @karelz, @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

@MichalStrehovsky
Copy link
Copy Markdown
Member

/ba-g one is #125953, another has a pr out by me, another is a timeout, all unrelated

@MichalStrehovsky MichalStrehovsky merged commit 2509dcb into main Mar 23, 2026
102 of 112 checks passed
@MichalStrehovsky MichalStrehovsky deleted the copilot/fix-readonlyspan-await-issue branch March 23, 2026 13:47
@agocke
Copy link
Copy Markdown
Member

agocke commented Mar 23, 2026

Huh. Little weird but Lgtm

eiriktsarpalis pushed a commit that referenced this pull request Mar 23, 2026
`Assert.Equal<byte>(Array.Empty<byte>(), await ...)` resolves the
`ReadOnlySpan<byte>` overload, causing CS4007 since the span can't be
preserved across the `await` boundary — breaking functional test builds
for `System.Net.Http.WinHttpHandler`.

## Description

- **`TrailingHeadersTest.cs`**: In
`Http2GetAsync_TrailingHeaders_NoData_EmptyResponseObserved`, extract
the awaited call into a local variable before `Assert.Equal`:

```csharp
// Before (broken):
Assert.Equal<byte>(Array.Empty<byte>(), await response.Content.ReadAsByteArrayAsync());

// After (fixed):
byte[] responseBody = await response.Content.ReadAsByteArrayAsync();
Assert.Equal(Array.Empty<byte>(), responseBody);
```

<!-- -->

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ## Problem
> 
> Building the functional tests for `System.Net.Http.WinHttpHandler`
fails with the following error:
> 
> ```
>
src\libraries\System.Net.Http.WinHttpHandler\tests\FunctionalTests\TrailingHeadersTest.cs(220,36):
error CS4007: Instance of type 'System.ReadOnlySpan<byte>' cannot be
preserved across 'await' or 'yield' boundary.
> ```
> 
> ### Root Cause
> 
> In
`src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/TrailingHeadersTest.cs`,
line 220, inside the
`Http2GetAsync_TrailingHeaders_NoData_EmptyResponseObserved` async test
method:
> 
> ```csharp
> Assert.Equal<byte>(Array.Empty<byte>(), await
response.Content.ReadAsByteArrayAsync());
> ```
> 
> `Assert.Equal<byte>(ReadOnlySpan<byte>, ReadOnlySpan<byte>)` overload
is being resolved here, which means `Array.Empty<byte>()` is implicitly
converted to a `ReadOnlySpan<byte>`. Since there is an `await`
expression in the same statement (as the second argument), the compiler
cannot preserve the `ReadOnlySpan<byte>` across the `await` boundary —
resulting in CS4007.
> 
> ## Fix
> 
> In the `Http2GetAsync_TrailingHeaders_NoData_EmptyResponseObserved`
method, extract the `await` call to a local variable **before** the
`Assert.Equal` call, so that no `ReadOnlySpan<byte>` lives across an
`await`:
> 
> ```csharp
> // Before (broken):
> Assert.Equal<byte>(Array.Empty<byte>(), await
response.Content.ReadAsByteArrayAsync());
> 
> // After (fixed):
> byte[] responseBody = await response.Content.ReadAsByteArrayAsync();
> Assert.Equal(Array.Empty<byte>(), responseBody);
> ```
> 
> The file to change is:
>
`src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/TrailingHeadersTest.cs`
> 
> The change is in the
`Http2GetAsync_TrailingHeaders_NoData_EmptyResponseObserved` method,
around line 220.
> 
> 
> The following is the prior conversation context from the user's chat
exploration (may be truncated):
> 
> User: in testing we started hitting following error, fix it:
src\libraries\System.Net.Http.WinHttpHandler\tests\FunctionalTests\TrailingHeadersTest.cs(220,36):
error CS4007: (NETCORE_ENGINEERING_TELEMETRY=Build) Instance of type
'System.ReadOnlySpan<byte>' cannot be preserved across 'await' or
'yield' boundary.
> 
> 
> Assistant: The issue is clear. On line 220, `Assert.Equal<byte>` is
being called inside an `async` method where the first argument is
`Array.Empty<byte>()` — which returns a `ReadOnlySpan<byte>` — and the
second argument is `await response.Content.ReadAsByteArrayAsync()`. The
`ReadOnlySpan<byte>` cannot be preserved across the `await` boundary.
> 
> The fix is to `await` the `ReadAsByteArrayAsync()` call first, store
it in a local variable, and then pass it to `Assert.Equal`.
> 
> Assistant: We're waiting for your response to the tool call dialog to
continue — please accept or reject the proposed pull request action so I
can proceed.
> 
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

📱 Kick off Copilot coding agent tasks wherever you are with [GitHub
Mobile](https://gh.io/cca-mobile-docs), available on iOS and Android.

Fixes #125952

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TrailingHeadersTest.cs: CS4007 ReadOnlySpan<byte> cannot be preserved across await boundary

5 participants