Install a Vue 3 error handler in non-production builds

Why

GitLab migrates its frontend from Vue 2 to Vue 3 page by page, behind feature flags, on @vue/compat. The two versions react differently when a component throws during render.

Vue 2 catches the error, logs it with console.error, and renders the component blank. The page keeps working. This happens in every build.

Vue 3 depends on whether an errorHandler is installed. In production, GitLab installs a Sentry handler (vueErrorHandler), so Vue 3 recovers the same way: it renders a comment node for the failing component and continues. In non-production builds no handler is installed, so Vue 3 rethrows. The throw unwinds through the parent's patch before instance.subTree is updated. Vnodes are left with el === null, and every later update fails with Cannot set properties of null (setting '__vnode'). The component tree is dead until the page reloads.

Feature specs (assets from the compile-test-assets job) and GDK, the local development environment, use the non-production build. So a component error that Vue 2 has tolerated for a long time breaks the page under Vue 3 in CI and locally, but not in production.

Concrete case: replying to a diff discussion on the merge request Rapid Diffs page. The reply placeholder note has no author, and noteable_note.vue throws TypeError: Cannot read properties of undefined (reading 'id'). Under Vue 2 the feature specs pass and log the error. Under Vue 3 the discussion list breaks, and spec/features/merge_request/user_comments_on_whitespace_hidden_diff_spec.rb and user_sees_avatar_on_diff_notes_spec.rb fail. The Vue 3 migration of that page was reverted for this reason (see References).

What

One file changes: app/assets/javascripts/lib/utils/vue3compat/vue.js. An else branch installs an errorHandler in non-production builds. The handler logs the error with logError from ~/lib/logger, in Vue 2's message format: [Vue warn]: Error in <info>: "<error>", then found in ---> <ComponentName>, then the error object.

Result: Vue 3 pages in development and CI behave like production and like Vue 2 when a component throws. The component renders empty, the page keeps working, and the error is one line in the browser console.

Jest is not affected. This block runs only when typeof jest === 'undefined', and Vue Test Utils keeps its own fail-fast handler.

How

The message copies Vue 2's format so one regexp matches both versions in console filters and allowlists.

Alternatives ruled out:

  • Adding these errors to the feature spec console filter (BROWSER_CONSOLE_ERROR_FILTER). The filter only affects the console check. The specs fail on their own assertions because the page is broken, so a filter cannot unblock them.
  • Installing the handler only under RAILS_ENV=test. GDK should behave like CI, so developers see the same console line the spec sees.
  • Fixing each crash before flipping each flag. That is the right fix per bug (for Rapid Diffs it is !255075 (merged)), but it blocks every migration on every pre-existing error that Vue 2 hides. This MR makes Vue 3 no stricter than Vue 2 in the same environment.

Trade-off: in GDK a Vue 3 crash is a console line instead of a broken page. That is the behaviour Vue 2 gives developers today. The errors stay visible. !255207 (closed) makes feature specs fail on any console error that is not in a baseline allowlist, so they are tracked rather than lost.

Related MRs:

  • !255209 (closed) re-applies the Rapid Diffs Vue 3 migration. It is stacked on this MR's branch and merges after it.
  • !255207 (closed) (console-error baseline) is set to depend on this MR.

How to verify

CI: the stacked MR's pipeline https://gitlab.com/gitlab-org/gitlab/-/pipelines/2843197576 runs the Rapid Diffs page under Vue 3 with this handler. Per the junit reports, all four examples that failed on master after the original migration pass:

An earlier run of the same combination: https://gitlab.com/gitlab-org/gitlab/-/pipelines/2843122054 (job https://gitlab.com/gitlab-org/gitlab/-/jobs/16462861924, 292 examples, 0 failures). The same examples fail in the vue3 jobs of !255207 (closed), which does not have this handler.

This MR's own pipeline only fails rspec unit clickhouse25 and clickhouse26, which fail on every pipeline today.

Locally:

  1. Check out !255209 (closed). It contains this change plus the two yml files that migrate the Rapid Diffs page.
  2. Run node scripts/frontend/infection_scanner/infection_scanner.mjs.
  3. Restart Vite: gdk restart vite rails-web.
  4. Enable the flag: Feature.enable(:vue3_migrate_mr_rapid_diffs).
  5. Open a merge request's Changes tab. Comment on a diff line. Reply to the thread.

Before this change: the reply never renders. The console shows the TypeError and Cannot set properties of null (setting '__vnode'). After: the reply renders when saved. The console shows one [Vue warn]: Error in render: "TypeError: ..." line and no __vnode errors.

Also run bin/rspec spec/features/merge_request/user_comments_on_whitespace_hidden_diff_spec.rb:49. It fails before and passes after.

Screenshots or screen recordings

No UI change.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist.

References

Edited by Miguel Rincon

Merge request reports

Loading
Loading