microsoft / microsoft/vscode

NES: `triggerAnimation()` returns a never-settling promise under reduced motion, hanging the onboarding loop

Open
#327,618 0 comments 0 reactions 1 assignee Claimed by @hediet View on GitHub
bug
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

Does this issue occur when all extensions are disabled?: No — the code path is reached via NES, which requires the GitHub Copilot extension.

- VS Code Version: 1.132.0 (built from source, `3ddd267c500`)
- OS Version: macOS 26.5.2

## Problem

Both NES animation helpers return a promise that never settles when reduced motion is enabled:

`src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts`

```ts
public triggerAnimation(): Promise {
if (this._accessibilityService.isMotionReduced()) {
return new Animation(null, null).finished;
}
...
```

The same pattern is in `inlineEditsViews/inlineEditsCollapsedView.ts`.

An `Animation` constructed with a `null` timeline starts in `idle` and is never played, so its `finished` promise stays pending forever. Verified in the VS Code renderer:

```js
const a = new Animation(null, null);
// { playState: "idle", timeline: "null", effect: "null",
// finishedPromise: "STILL-PENDING-after-1500ms" }
```

## Impact

`inlineEditsNewUsers.ts` awaits it in a loop:

```ts
case UserKind.FirstTime: {
for (let i = 0; i < 3 && !token.isCancellationRequested; i++) {
await this._indicator.get()?.triggerAnimation(); // never resolves
await timeout(500);
}
break;
}
```

For a first-time NES user who has reduced motion enabled, the onboarding routine blocks permanently on the first iteration. Because this runs inside `runOnChangeWithCancellationToken`, cancelling the token does not unblock an `await` on a never-settling promise, so the async frame and its closure stay alive — one hung promise chain per suggestion shown, for the lifetime of the editor.

## Expected

Return an already-resolved promise (or change the signature to `Promise` / `Promise`) when motion is reduced, so callers can `await` it safely.

Suppressing the animation under reduced motion is correct — only the promise is wrong.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.