dotnet / dotnet/vscode-csharp

Debugger: first Step Over after Hot Reload inside the patched method's active frame runs away — process runs to completion, re-bound breakpoint never hit (IncompleteStep)

Open
#9,501 1 comment 0 reactions 0 assignees View on GitHub
Debugger
Dominant language
TypeScript
Stars
3.1k
Forks
737
Avg merge
17h 56m
Merged PRs (30d)
21

Description

## Environment

- VS Code 1.127.0
- C# extension 2.140.9 (also observed with C# Dev Kit 3.20.199 installed)
- .NET SDK 10.0.300, target `net10.0`
- macOS 26.5 (arm64)
- Debugger: vsdbg (default `coreclr` adapter)

## Summary

When the debugger is paused **inside a method that has just been hot-patched** (Hot Reload applied while stopped in that frame), and no step command has been issued earlier in the session, the **first** `Step Over` silently loses the stepper: instead of stopping on the next line, the process resumes and **runs to completion**. The breakpoint that was re-bound after the reload never fires, and the debug session ends. vsdbg emits its own `VS/Diagnostics/Debugger/IncompleteStep` telemetry event at that moment.

Reproduced 3/3 in fresh sessions clicking the UI manually; the DAP trace confirms `next` → `continued` → no `stopped` → `exited`.

## Repro

`Calculator.cs`:

```csharp
public class Calculator
{
public int Add(int a, int b)
{
int result = a - b; // line 12 — deliberately wrong
return result; // line 13
}
}
```

xUnit test:

```csharp
[Fact]
public void Add_ReturnsSum() => Assert.Equal(5, new Calculator().Add(2, 3));
```

Steps (all in the UI, fresh debug session, **no stepping before step 4**):

1. Set a breakpoint on line 13 (`return result;`). Debug the test; it stops on line 13 with `a=2 b=3 result=-1`.
2. While paused: edit line 12 to `int result = a + b;` and save.
3. Click **Hot Reload** (the flame button in the floating debug toolbar). The reload applies cleanly (no ENC errors).
4. Right-click line 12 → **Jump to Cursor** (Set Next Statement back into the patched method). The debugger stops on line 12 as expected.
5. Press **Step Over** (F10) once.

## Expected

Stop on line 13 (or at worst anywhere in user code), session still paused.

## Actual

The debug session ends: the step escapes, the process runs to completion (exit code 0), the breakpoint on line 13 never fires. The DAP trace shows `next` → `continued` → `exited` with no `stopped` event in between, and vsdbg logs a `VS/Diagnostics/Debugger/IncompleteStep` telemetry event.

## Additional observations

- Does **not** reproduce if at least one Step of any kind was executed earlier in the same session, *before* the Hot Reload (2/2). A "warmed-up" stepper survives; then a different (milder) issue shows instead: the first post-reload step in the patched frame moves the instruction pointer but does not actually execute the source line (locals keep their previous values); from the second step onwards the new body executes normally. I can file that separately if useful.
- Inserting a 5-second delay between the reload and the jump/step does not help — this is not a race with delta application.
- Without any Hot Reload involved, repeated Jump-to-Cursor + Step Over rounds in the same paused frame are fully deterministic — the issue is specific to stepping in the (stale) active frame of an EnC-patched method.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the scenario with Calculator.cs and the xUnit Add_ReturnsSum test in a fresh VS Code debug session, following the Hot Reload, Jump to Cursor, and first Step Over sequence. Inspect the DAP trace around next, continued, stopped, and exited, along with the vsdbg IncompleteStep event. Done means the first post-reload Step Over stops in user code, ideally on line 13, without ending the session.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, typescript, vscode
Domain
developer-experience, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
46/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.