Change:
Stepping using the debug adapter that I helped develop has been broken by a VS Code change between 1.78.2 and 1.79.0. I used a to log the messages sent back and forth using the same extension version, but different VS Code versions. I attached the logs below. After diffing the two files, the only difference is that the "scopes" request isn't sent to the adapter after the "next" request reports a stopped event. The end result is that the debugging UI in VS Code is stuck in "running" mode even though the debug adapter is stopped, and the user has no way to advance. See
I think this is related to the fact that you send the "stopped" event before the "next" response. We should be able to support that but as a workaround you could respond to "next" before stopping the debugger.
Thanks for the quick response I will look into that workaround. In what cases is tit required? Any type of step?
I see the issue but it will be too much of a risky change to get into our next stable release. I will try to address it in July but I'd recommend working around it in your extension.
I understand. I implemented the workaround in my extension and initial tests show that it works, so thanks for making me aware of that.
For my own reference, I think it should be ok to do immediately instead of waiting for the response.
Hi what are some verification steps for this issue?
<!-- AUTHORVERIFICATIONREQUEST --This bug has been fixed in the latest release of ! you can help us out by commenting if things are now working as expected. If things still don't seem right, please ensure you're on version of Insiders (today's or later - you can use in the command palette to check), and leave a comment letting us know what isn't working as expected. Happy Coding!
I verified this by hacking mock-debug, but are you able to verify the change in Insiders, or have you moved on to not rely on that behavior anymore?
I've modified my extension to implement your workaround. I can download an old version of the extension to test this if you want.
It's ok, I think I've got it now

Discussion:
// DA events private readonly _onDidExitAdapter = new Emitter<AdapterEndEvent>(); private debugAdapter: IDebugAdapter | null; <ins> private stoppedSinceLastStep = false; </ins> private toDispose: IDisposable[] = [];

break; case 'stopped': this.didReceiveStoppedEvent = true;		// telemetry: remember that debugger stopped successfully <ins> this.stoppedSinceLastStep = true; </ins> this._onDidStop.fire(<DebugProtocol.StoppedEvent>event); break; case 'continued':

} async next(args: DebugProtocol.NextArguments): Promise<DebugProtocol.NextResponse | undefined> { <ins> this.stoppedSinceLastStep = false; </ins> const response = await this.send('next', args); <del> this.fireSimulatedContinuedEvent(args.threadId); </del> <ins> if (!this.stoppedSinceLastStep) { this.fireSimulatedContinuedEvent(args.threadId); } </ins> return response; } async stepIn(args: DebugProtocol.StepInArguments): Promise<DebugProtocol.StepInResponse | undefined> { <ins> this.stoppedSinceLastStep = false; </ins> const response = await this.send('stepIn', args); <del> this.fireSimulatedContinuedEvent(args.threadId); </del> <ins> if (!this.stoppedSinceLastStep) { this.fireSimulatedContinuedEvent(args.threadId); } </ins> return response; } async stepOut(args: DebugProtocol.StepOutArguments): Promise<DebugProtocol.StepOutResponse | undefined> { <ins> this.stoppedSinceLastStep = false; </ins> const response = await this.send('stepOut', args); <del> this.fireSimulatedContinuedEvent(args.threadId); </del> <ins> if (!this.stoppedSinceLastStep) { this.fireSimulatedContinuedEvent(args.threadId); } </ins> return response; } async continue(args: DebugProtocol.ContinueArguments): Promise<DebugProtocol.ContinueResponse | undefined> { <ins> this.stoppedSinceLastStep = false; </ins> const response = await this.send<DebugProtocol.ContinueResponse>('continue', args); if (response && response.body && response.body.allThreadsContinued !== undefined) { this.allThreadsContinued = response.body.allThreadsContinued; } <del> this.fireSimulatedContinuedEvent(args.threadId, this.allThreadsContinued); </del> <ins> if (!this.stoppedSinceLastStep) { this.fireSimulatedContinuedEvent(args.threadId, this.allThreadsContinued); } </ins> return response; }

async restartFrame(args: DebugProtocol.RestartFrameArguments, threadId: number): Promise<DebugProtocol.RestartFrameResponse | undefined> { if (this.capabilities.supportsRestartFrame) { <ins> this.stoppedSinceLastStep = false; </ins> const response = await this.send('restartFrame', args); <del> this.fireSimulatedContinuedEvent(threadId); </del> <ins> if (!this.stoppedSinceLastStep) { this.fireSimulatedContinuedEvent(threadId); } </ins> return response; } return Promise.reject(new Error('restartFrame not supported'));

async stepBack(args: DebugProtocol.StepBackArguments): Promise<DebugProtocol.StepBackResponse | undefined> { if (this.capabilities.supportsStepBack) { <ins> this.stoppedSinceLastStep = false; </ins> const response = await this.send('stepBack', args); <del> this.fireSimulatedContinuedEvent(args.threadId); </del> <ins> if (!this.stoppedSinceLastStep) { this.fireSimulatedContinuedEvent(args.threadId); } </ins> return response; } return Promise.reject(new Error('stepBack not supported'));

async reverseContinue(args: DebugProtocol.ReverseContinueArguments): Promise<DebugProtocol.ReverseContinueResponse | undefined> { if (this.capabilities.supportsStepBack) { <ins> this.stoppedSinceLastStep = false; </ins> const response = await this.send('reverseContinue', args); <del> this.fireSimulatedContinuedEvent(args.threadId); </del> <ins> if (!this.stoppedSinceLastStep) { this.fireSimulatedContinuedEvent(args.threadId); } </ins> return response; } return Promise.reject(new Error('reverseContinue not supported'));

async goto(args: DebugProtocol.GotoArguments): Promise<DebugProtocol.GotoResponse | undefined> { if (this.capabilities.supportsGotoTargetsRequest) { <ins> this.stoppedSinceLastStep = false; </ins> const response = await this.send('goto', args); <del> this.fireSimulatedContinuedEvent(args.threadId); </del> <ins> if (!this.stoppedSinceLastStep) { this.fireSimulatedContinuedEvent(args.threadId); } </ins> return response; }