Skip to content

[Bug] Uncaught (in promise) Canceled: Canceled when navigating cross-file from peek widget #5294

@smonn

Description

@smonn

Reproducible in vscode.dev or in VS Code Desktop?

  • Not reproducible in vscode.dev or VS Code Desktop

Reproducible in the monaco editor playground?

Monaco Editor Playground Link

No response

Monaco Editor Playground Code

Reproduction Steps

  1. Open a Monaco editor with at least two TypeScript files where one import from the other
  2. Press Shift+F12 (or right-click → "Find All References") on a symbol so the peek widget opens showing references in the other file
  3. Double-click one of the references in the peek to navigate to it
  4. Observe Uncaught (in promise) Canceled: Canceled in the console

A related error also appears on tab switches after cross-file navigation, originating from wordHighlighter.js:restore().

Actual (Problematic) Behavior

When editor.setModel() is called (triggered by the navigation), the onDidChangeModel event fires. The peek widget's listener calls dispose(), which calls CancellationTokenSource.cancel(), which rejects a Promise that has no .catch() handler.

Expected Behavior

No unhandled promise rejection. The peek widget should either catch the CancellationError it produces on disposal, or dispose cleanly before the model switch occurs.

Additional Context

I didn't find an exact match for this, so reporting as a distinct issue.

When a peek widget is open (e.g. via "Go to References" / Shift+F12, or "Peek Definition" / Alt+F12) and the user double-clicks a result to navigate to a different file, an unhandled promise rejection is produced:

  Uncaught (in promise) Canceled: Canceled
      at new hr (editor.api.js)
      at cancel (editor.api.js)
      at dispose (editor.api.js)
      ...
      at _deliver (editor.api.js)
      at _deliverQueue (editor.api.js)

The navigation itself works correctly. The editor switches to the target file at the right position. The error is purely console noise, but it breaks test runners that treat unhandled rejections as failures (Jasmine, Vitest with dangerouslyIgnoreUnhandledErrors: false, etc.).

This appears to be the same root cause as #2382 (onMouseLeave) and #4554 / #4767 (inline completions): Promise chains inside Monaco that are missing .catch() handlers. The fix in #2382 added a no-op .catch() to the specific promise. It's likely the same approach would likely fix this case.

Workarounds tried

  • event.preventDefault() on the unhandledrejection event (with and without { capture: true }) does not suppress DevTools console output in Chrome or Firefox.
  • editor.trigger('', 'closeReferenceSearch', null) before switching models has no effect.
  • editor.getContribution('editor.contrib.referencesController')?.closeWidget?.() before switching models (per [Feature Request] Close peek problem/definition window programmatically #4407 pattern) has no effect. The rejection either occurs during closeWidget itself or the controller isn't found. Tried something similar for wordHighlighter but not able to make it work.
Patch for v0.55.1

This patch eliminates the logged errors on my end.

diff --git a/esm/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.js b/esm/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.js
index 095246af95d4f4037610f3b317a0c2667467c810..a9c1f25541cb9747907882045311cb3adf9a2d06 100644
--- a/esm/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.js
+++ b/esm/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.js
@@ -162,7 +162,7 @@ let ReferencesController = class ReferencesController {
             });
         }, error => {
             this._notificationService.error(error);
-        });
+        }).catch(onUnexpectedError);
     }
     changeFocusBetweenPreviewAndReferences() {
         if (!this._widget) {
diff --git a/esm/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.js b/esm/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.js
index 9687c56e44f9dac9525009b8639f45d02fdb2e93..2b968a845b194c90c64ace487ee181ec58744c58 100644
--- a/esm/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.js
+++ b/esm/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.js
@@ -171,7 +171,7 @@ let WordHighlighter = class WordHighlighter {
                 // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;)
                 return;
             }
-            this.runDelayer.trigger(() => { this._onPositionChanged(e); });
+            this.runDelayer.trigger(() => { this._onPositionChanged(e); }).catch(onUnexpectedError);
         }));
         this.toUnhook.add(editor.onDidFocusEditorText((e) => {
             if (this.occurrencesHighlightEnablement === 'off') {
@@ -179,7 +179,7 @@ let WordHighlighter = class WordHighlighter {
                 return;
             }
             if (!this.workerRequest) {
-                this.runDelayer.trigger(() => { this._run(); });
+                this.runDelayer.trigger(() => { this._run(); }).catch(onUnexpectedError);
             }
         }));
         this.toUnhook.add(editor.onDidChangeModelContent((e) => {
@@ -192,7 +192,7 @@ let WordHighlighter = class WordHighlighter {
                 this._stopSingular();
             }
             else if (WordHighlighter_1.query) {
-                this._run();
+                this._run().catch(onUnexpectedError);
             }
         }));
         this.toUnhook.add(editor.onDidChangeConfiguration((e) => {
@@ -246,7 +246,7 @@ let WordHighlighter = class WordHighlighter {
         this.renderDecorationsTimer = undefined;
         // if there is a query already, highlight off that query
         if (WordHighlighter_1.query) {
-            this._run();
+            this._run().catch(onUnexpectedError);
         }
     }
     hasDecorations() {
@@ -257,7 +257,7 @@ let WordHighlighter = class WordHighlighter {
             return;
         }
         this.runDelayer.cancel();
-        this.runDelayer.trigger(() => { this._run(false, delay); });
+        this.runDelayer.trigger(() => { this._run(false, delay); }).catch(onUnexpectedError);
     }
     stop() {
         if (this.occurrencesHighlightEnablement === 'off') {
@@ -357,7 +357,7 @@ let WordHighlighter = class WordHighlighter {
         if (this.editor.hasTextFocus()) {
             if (this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell && WordHighlighter_1.query?.modelInfo?.modelURI.scheme !== Schemas.vscodeNotebookCell) { // clear query if focused non-nb editor
                 WordHighlighter_1.query = null;
-                this._run(); // TODO: @Yoyokrazy -- investigate why we need a full rerun here. likely addressed a case/patch in the first iteration of this feature
+                this._run().catch(onUnexpectedError); // TODO: @Yoyokrazy -- investigate why we need a full rerun here. likely addressed a case/patch in the first iteration of this feature
             }
             else { // remove modelInfo to account for nb cell being disposed
                 if (WordHighlighter_1.query?.modelInfo) {
@@ -414,7 +414,7 @@ let WordHighlighter = class WordHighlighter {
             this._stopAll();
             return;
         }
-        this._run();
+        this._run().catch(onUnexpectedError);
     }
     _getWord() {
         const editorSelection = this.editor.getSelection();

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions