You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is your feature request related to a problem? Please describe.
shell/components/ResourceList/Masthead.vue does real work inside data() — it reads Vue globalProperties, calls into the extension manager, iterates the registered extensions and hits Vuex getters, all to decide a single route object:
This bit us for real in #18634: on Rancher 2.11–2.13 the extension manager lives under $plugin rather than $extension, so this.$extension is undefined in an extension-bundled copy of this component and the dereference throws. Because the throw happens in data(), it doesn't degrade one button — it takes the entire route render down and the user gets a blank page with only a console error to go on. That failure mode is the subject of the epic in #11807: exceptions in data() are swallowed into an empty page, a regression from Vue 2 behaviour that we still live with.
#18664 fixes the immediate crash with a null-guard at the call site (this.$extension?.getPlugins?.() || {}), which is the right minimal fix to backport. But as raised in review (#18664 (comment)), the guard treats the symptom. The underlying problem is that data() is being used for derivation and dependency access instead of just declaring initial component state, so any new fragile dependency added there gets the same blast radius for free.
This is not isolated to Masthead.vue — roughly 474 .vue files under shell/ and pkg/ declare a data() block, and a meaningful number of them touch $store, $route or globalProperties inside it. Masthead.vue is just the highest-leverage instance: it's the header for every ResourceList, used by ~20 components directly and by every core and extension list page in the product. A crash here is a blank page on a very large slice of the UI.
Describe the solution you'd like
Move the create-route derivation out of data() in Masthead.vue:
formRoute, yamlRoute, overrideCreateLocationByExtension and hasEditComponent are all pure functions of props, $route and store state — they should be computed, not seeded state. Nothing mutates them after creation, so there's no reason for them to be reactive data at all.
data() should be left declaring plain initial state only.
Anything that genuinely needs to be seeded imperatively should be seeded from a lifecycle hook (created/fetch), where a throw is contained and can be surfaced properly instead of blanking the route.
Two secondary things worth folding in while the file is open:
Once the derivation lives in a computed, the extension-manager access is evaluated lazily and after mount, which means the $extension/$plugin compat shim in @shell/pkg/auto-import has actually had a chance to run. The null-guard from fix extensions issue with hard refresh on a list page route #18664 should stay regardless (the store seeds the manager keys with an empty object before swapping in the real one, so "present but not usable" is a genuine state), but it stops being the only thing standing between an extension and a white screen.
Leave the null-guard as the fix and close it out. Cheapest option and it does resolve bug: Hard-refreshing the browser on an extension list route white-screens #18634, but it leaves the anti-pattern in the most widely-rendered masthead in the product, so the next fragile dependency added to data() reproduces the same class of bug.
Sweep all ~474 data() blocks at once. Too big and too risky as a single change. Better to do Masthead.vue first as the highest-impact instance, and use it as the reference pattern for a follow-up sweep under [Epic] Vue3: Exceptions thrown in data result in blank page #11807 if we want to go further.
Is your feature request related to a problem? Please describe.
shell/components/ResourceList/Masthead.vuedoes real work insidedata()— it reads Vue globalProperties, calls into the extension manager, iterates the registered extensions and hits Vuex getters, all to decide a single route object:This bit us for real in #18634: on Rancher 2.11–2.13 the extension manager lives under
$pluginrather than$extension, sothis.$extensionisundefinedin an extension-bundled copy of this component and the dereference throws. Because the throw happens indata(), it doesn't degrade one button — it takes the entire route render down and the user gets a blank page with only a console error to go on. That failure mode is the subject of the epic in #11807: exceptions indata()are swallowed into an empty page, a regression from Vue 2 behaviour that we still live with.#18664 fixes the immediate crash with a null-guard at the call site (
this.$extension?.getPlugins?.() || {}), which is the right minimal fix to backport. But as raised in review (#18664 (comment)), the guard treats the symptom. The underlying problem is thatdata()is being used for derivation and dependency access instead of just declaring initial component state, so any new fragile dependency added there gets the same blast radius for free.This is not isolated to
Masthead.vue— roughly 474.vuefiles undershell/andpkg/declare adata()block, and a meaningful number of them touch$store,$routeor globalProperties inside it.Masthead.vueis just the highest-leverage instance: it's the header for everyResourceList, used by ~20 components directly and by every core and extension list page in the product. A crash here is a blank page on a very large slice of the UI.Describe the solution you'd like
Move the create-route derivation out of
data()inMasthead.vue:formRoute,yamlRoute,overrideCreateLocationByExtensionandhasEditComponentare all pure functions of props,$routeand store state — they should becomputed, not seeded state. Nothing mutates them after creation, so there's no reason for them to be reactive data at all.data()should be left declaring plain initial state only.created/fetch), where a throw is contained and can be surfaced properly instead of blanking the route.Two secondary things worth folding in while the file is open:
$extension/$plugincompat shim in@shell/pkg/auto-importhas actually had a chance to run. The null-guard from fix extensions issue with hard refresh on a list page route #18664 should stay regardless (the store seeds the manager keys with an empty object before swapping in the real one, so "present but not usable" is a genuine state), but it stops being the only thing standing between an extension and a white screen.Describe alternatives you've considered
data()reproduces the same class of bug.$extension/$pluginis always aliased early enough. Investigated in bug: Hard-refreshing the browser on an extension list route white-screens #18634 and it's structurally impossible for this call site — the shim finds the app viadocument.getElementById('app').__vue_app__, and Vue only assigns__vue_app__after the first render, which is the very render that runs Masthead'sdata(). (The sibling case reached through Vuex root state, bug: TypeError: Cannot read properties of undefined (reading 'getPlugins') at get isProdRegistrationV2TopLevelProductResoure (resource-class.js:1422) #18633, is fixable in the shim — that one is not blocked by this.)data()blocks at once. Too big and too risky as a single change. Better to doMasthead.vuefirst as the highest-impact instance, and use it as the reference pattern for a follow-up sweep under [Epic] Vue3: Exceptions thrown indataresult in blank page #11807 if we want to go further.Additional context
dataresult in blank page #11807 — Exceptions thrown indataresult in blank page$extension/$plugincompat issue, fixable in the shim: bug: TypeError: Cannot read properties of undefined (reading 'getPlugins') at get isProdRegistrationV2TopLevelProductResoure (resource-class.js:1422) #18633shell/components/ResourceList/Masthead.vueSuggested labels:
area/extensions,kind/enhancement(or your tech-debt equivalent),area/engineering.