dotnet / dotnet/roslyn

Debugger stepping incorrect in async method: execution flow jumps to `else` block after `return`

Open
#84,464 3 comments 0 reactions 0 assignees View on GitHub
Area-Interactive Interactive-Debugging
Dominant language
C#
Stars
20.7k
Forks
4.3k
PR merge metrics
PR metrics pending

Description

### Description

I have encountered an issue with the debugger stepping behavior in an `async` method. When the code successfully executes a `return` statement inside a `try` block (which is nested within an `if` block), the debugger step-over (F10) action visually jumps the instruction pointer into the `else` block of the outer `if` statement.

Despite the debugger highlighting the code inside the `else` block, the method actually returns the correct value (from the `if` block), indicating that the compiled code execution is likely correct, but the sequence points or debugger information generated is misleading.

This behavior is confusing as it suggests that the fallback logic in the `else` block is being executed when it is not.

### Reproduction Steps

I have attached a minimal reproduction project.

1. Open the attached solution.
2. Navigate to the `ShortenAsync` method (in `ShortLinkService.cs`).
3. Set a breakpoint on the line:
```csharp
var resultUrl = await AddOrGetShortUrlFromDbAsync(dbContext, newLink);
```
4. Start debugging.
5. When the breakpoint is hit, Step Over (F10).

**Code snippet for context:**

```csharp
if (urlShortenerProviderResult.Success)
{
// ... object initialization ...
try
{
var resultUrl = await AddOrGetShortUrlFromDbAsync(dbContext, newLink); // <--- Breakpoint here
return resultUrl; // <--- Step Over this
}
catch (Exception e)
{
// ... logging ...
return shortUrl;
}
}
else
{
// Debugger jumps HERE after the return above
_logger.LogError("Can't get a short url from the provider...");
return longUrl;
}
```

[DotnetBugRepro-master.zip](https://github.com/user-attachments/files/24179096/DotnetBugRepro-master.zip)

### Expected behavior

After stepping over `return resultUrl;`, the debugger should exit the method or move to the closing brace of the method context. It should not highlight any lines inside the `else` block.

### Actual behavior

The debugger execution pointer jumps to the `else` block (e.g., to the logging statement or the `return longUrl` line), creating the false impression that the `if (Success)` check failed or that the flow fell through.

### Regression?

_No response_

### Known Workarounds

_No response_

### Configuration

.NET 10.0.101 x64
Windows 11
x64
Console App

### Other information

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with the attached DotnetBugRepro project and inspect ShortLinkService.cs, especially ShortenAsync and its async try/return inside the if block. Run the reproduction on .NET 10.0.101, step over the marked await and return, and compare the debugger location with the actual returned value; done means the pointer does not highlight the outer else block after the successful return.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.