diff --git a/.changeset/feature-flag-invalid-warning.md b/.changeset/feature-flag-invalid-warning.md new file mode 100644 index 0000000000..590a4bf959 --- /dev/null +++ b/.changeset/feature-flag-invalid-warning.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-defaults': patch +--- + +Invalid feature flag declarations are now treated as warnings rather than errors, letting the app load normally. diff --git a/packages/frontend-app-api/src/wiring/apiFactories.test.ts b/packages/frontend-app-api/src/wiring/apiFactories.test.ts index 34fa2fa3ee..9198af6f9c 100644 --- a/packages/frontend-app-api/src/wiring/apiFactories.test.ts +++ b/packages/frontend-app-api/src/wiring/apiFactories.test.ts @@ -177,6 +177,8 @@ describe('registerFeatureFlagDeclarationsInHolder', () => { code: 'FEATURE_FLAG_INVALID', context: { pluginId: 'my-plugin', flagName: 'mod/invalid' }, }); + expect(errors[0].message).toContain("Module for plugin 'my-plugin'"); + expect(errors[0].message).toContain("'mod/invalid'"); }); it('should isolate non-validation errors thrown by registerFlag', () => { diff --git a/packages/frontend-app-api/src/wiring/apiFactories.ts b/packages/frontend-app-api/src/wiring/apiFactories.ts index 3196c5de46..1fdc747f51 100644 --- a/packages/frontend-app-api/src/wiring/apiFactories.ts +++ b/packages/frontend-app-api/src/wiring/apiFactories.ts @@ -183,28 +183,22 @@ function registerFeatureFlagDeclarations( collector: ErrorCollector, ) { for (const feature of features) { + let pluginId: string | undefined; + let flags: Array<{ name: string; description?: string }> | undefined; + let source: string | undefined; + if (OpaqueFrontendPlugin.isType(feature)) { - const pluginId = feature.id; - for (const flag of OpaqueFrontendPlugin.toInternal(feature) - .featureFlags) { - try { - featureFlagApi.registerFlag({ - name: flag.name, - description: flag.description, - pluginId, - }); - } catch (error) { - collector.report({ - code: 'FEATURE_FLAG_INVALID', - message: `Plugin '${pluginId}' declared invalid feature flag '${flag.name}': ${error}`, - context: { pluginId, flagName: flag.name, error: error as Error }, - }); - } - } + pluginId = feature.id; + flags = OpaqueFrontendPlugin.toInternal(feature).featureFlags; + source = 'Plugin'; + } else if (isInternalFrontendModule(feature)) { + pluginId = feature.pluginId; + flags = toInternalFrontendModule(feature).featureFlags; + source = 'Module for plugin'; } - if (isInternalFrontendModule(feature)) { - const pluginId = feature.pluginId; - for (const flag of toInternalFrontendModule(feature).featureFlags) { + + if (pluginId && flags && source) { + for (const flag of flags) { try { featureFlagApi.registerFlag({ name: flag.name, @@ -214,7 +208,7 @@ function registerFeatureFlagDeclarations( } catch (error) { collector.report({ code: 'FEATURE_FLAG_INVALID', - message: `Plugin '${pluginId}' declared invalid feature flag '${flag.name}': ${error}`, + message: `${source} '${pluginId}' declared invalid feature flag '${flag.name}': ${error}`, context: { pluginId, flagName: flag.name, error: error as Error }, }); } diff --git a/packages/frontend-defaults/src/maybeCreateErrorPage.tsx b/packages/frontend-defaults/src/maybeCreateErrorPage.tsx index 8c058b838f..a5463d4846 100644 --- a/packages/frontend-defaults/src/maybeCreateErrorPage.tsx +++ b/packages/frontend-defaults/src/maybeCreateErrorPage.tsx @@ -26,6 +26,7 @@ const DEFAULT_WARNING_CODES: Array = [ 'EXTENSION_OUTPUT_IGNORED', 'EXTENSION_BOOTSTRAP_PREDICATE_IGNORED', 'EXTENSION_BOOTSTRAP_API_UNAVAILABLE', + 'FEATURE_FLAG_INVALID', ]; function AppErrorItem(props: { error: AppError }): JSX.Element {