codeant-ai-for-open-source[bot] commented on code in PR #37378:
URL: https://github.com/apache/superset/pull/37378#discussion_r2756488037
##########
superset-frontend/src/features/themes/ThemeModal.test.tsx:
##########
@@ -304,10 +357,52 @@ test('validates JSON format and enables save button',
async () => {
const nameInput = screen.getByPlaceholderText('Enter theme name');
await userEvent.type(nameInput, 'Test Theme');
+ await addValidJsonData();
- const saveButton = await screen.findByRole('button', { name: 'Add' });
+ // Wait for validation to complete and button to become enabled
+ await waitFor(
+ () => {
+ const saveButton = screen.getByRole('button', { name: 'Add' });
+ expect(saveButton).toBeEnabled();
+ },
+ { timeout: 10000 },
+ );
+});
+
+test('warnings do not block save - unknown tokens allow save with warnings',
async () => {
+ // First verify the test data actually produces warnings (not errors)
+ const testTheme = {
+ token: { colorPrimary: '#1890ff', unknownTokenName: 'value' },
+ };
+ const validationResult = validateTheme(testTheme);
+ expect(validationResult.valid).toBe(true); // No errors
+ expect(validationResult.warnings.length).toBeGreaterThan(0); // Has warnings
+ expect(validationResult.warnings[0].tokenName).toBe('unknownTokenName');
Review Comment:
**Suggestion:** The test for warning behavior assumes that the first element
in the `warnings` array corresponds to a specific token, which makes the test
brittle if the validator ever changes the order of warnings; instead, it should
assert that *some* warning has the expected token name. [logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ❌ This unit test may become flaky intermittently.
- ⚠️ CI pipelines show non-deterministic failures.
- ⚠️ Slows developer debugging of validator changes.
```
</details>
```suggestion
expect(
validationResult.warnings.some(
warning => warning.tokenName === 'unknownTokenName',
),
).toBe(true);
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Open superset-frontend/src/features/themes/ThemeModal.test.tsx and find
the validation
assertions located at ThemeModal.test.tsx:377-380. validateTheme is imported
at
ThemeModal.test.tsx:25.
2. Run only the test 'warnings do not block save - unknown tokens allow save
with
warnings' (the test added starting at ThemeModal.test.tsx:372) using the
test runner
(e.g., yarn test ThemeModal.test.tsx -t "warnings do not block save").
3. validateTheme(testTheme) (ThemeModal.test.tsx:377) returns an object with
warnings
array. If the validator implementation changes the order of warnings (or
accumulates
warnings from different code paths), the exact element at warnings[0] may
not be the
unknownTokenName and the expectation at ThemeModal.test.tsx:380 fails.
4. The failing assertion is a brittle check of a specific index rather than
membership;
replacing it with a .some(...) membership check (as proposed) prevents
order-dependent
flakes.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset-frontend/src/features/themes/ThemeModal.test.tsx
**Line:** 380:380
**Comment:**
*Logic Error: The test for warning behavior assumes that the first
element in the `warnings` array corresponds to a specific token, which makes
the test brittle if the validator ever changes the order of warnings; instead,
it should assert that *some* warning has the expected token name.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
##########
superset-frontend/src/theme/utils/antdTokenNames.ts:
##########
@@ -0,0 +1,116 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { theme } from 'antd';
+
+/**
+ * Superset-specific custom tokens that extend Ant Design's token system.
+ * These keys are derived from the SupersetSpecificTokens interface to ensure
consistency.
+ */
+const SUPERSET_CUSTOM_TOKENS: Set<string> = new Set([
+ // Font extensions
+ 'fontSizeXS',
+ 'fontSizeXXL',
+ 'fontWeightNormal',
+ 'fontWeightLight',
+ 'fontWeightStrong',
Review Comment:
**Suggestion:** The token `fontWeightStrong` is an Ant Design core design
token but is incorrectly listed as a Superset-specific custom token, so
`isSupersetCustomToken` and `getAllValidTokenNames` will wrongly classify it as
Superset-only instead of antd, which can break any logic that treats custom
tokens differently from native antd tokens. [logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ❌ Theme token classification incorrect during validation.
- ⚠️ ThemeModal may label token as Superset-specific.
- ❌ Token lists returned by getAllValidTokenNames wrong.
- ⚠️ Validation/filtering may drop or mishandle styling token.
```
</details>
```suggestion
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Import and call getAllValidTokenNames() from
superset-frontend/src/theme/utils/antdTokenNames.ts (function defined at
lines 100-115) in
a unit test or small script. This invokes getValidTokenNames() at lines
63-76.
2. getValidTokenNames() (lines 63-76) calls theme.getDesignToken() and
builds a set
containing Ant Design keys plus SUPERSET_CUSTOM_TOKENS
(SUPERSET_CUSTOM_TOKENS declared at
lines 25-31).
3. getAllValidTokenNames() computes antdTokens via
Array.from(allTokens).filter(t =>
!isSupersetCustomToken(t)) (lines 106-108). isSupersetCustomToken() (lines
92-94) returns
true for 'fontWeightStrong' because it is present in SUPERSET_CUSTOM_TOKENS
(line 31).
4. Observe that 'fontWeightStrong' is absent from the returned antdTokens
array and
appears only in supersetTokens. Any consumer relying on antdTokens or
isSupersetCustomToken() (e.g., theme validation / token filtering code that
imports these
functions) will see the token misclassified. The improved change removes
'fontWeightStrong' from SUPERSET_CUSTOM_TOKENS (so isSupersetCustomToken()
will return
false), correcting classification.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset-frontend/src/theme/utils/antdTokenNames.ts
**Line:** 31:31
**Comment:**
*Logic Error: The token `fontWeightStrong` is an Ant Design core design
token but is incorrectly listed as a Superset-specific custom token, so
`isSupersetCustomToken` and `getAllValidTokenNames` will wrongly classify it as
Superset-only instead of antd, which can break any logic that treats custom
tokens differently from native antd tokens.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
##########
superset-frontend/src/features/themes/ThemeModal.test.tsx:
##########
@@ -418,6 +513,19 @@ test('saves changes when clicking Save button in unsaved
changes alert', async (
const nameInput = screen.getByPlaceholderText('Enter theme name');
await userEvent.type(nameInput, 'Modified Theme');
+ await addValidJsonData();
+
+ // Wait for validation to complete before canceling
+ await waitFor(
+ () => {
+ const addButton = screen.getByRole('button', { name: 'Add' });
+ expect(addButton).toBeEnabled();
+ },
+ { timeout: 10000 },
+ );
+
+ // Give extra time for all state updates to complete
+ await new Promise(resolve => setTimeout(resolve, 500));
Review Comment:
**Suggestion:** Using a hard-coded 500ms timeout to "give extra time"
introduces unnecessary test delay and potential flakiness, since the test is
not tied to any specific condition and may still race on slower environments;
it is safer to rely solely on explicit waits for the relevant DOM state.
[possible bug]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ⚠️ Slows ThemeModal test by 500ms each run.
- ⚠️ Increases CI time across many test runs.
- ⚠️ Can still cause intermittent flakiness on slow runners.
```
</details>
```suggestion
// All necessary state updates are awaited via the previous waitFor, no
extra timeout needed.
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Open superset-frontend/src/features/themes/ThemeModal.test.tsx and locate
the
artificial sleep at ThemeModal.test.tsx:527-528 inside the test 'saves
changes when
clicking Save button in unsaved changes alert' (test block beginning at
~ThemeModal.test.tsx:513).
2. Run the test file or full test suite (e.g., yarn test). The test will
always pause for
500ms at ThemeModal.test.tsx:528, adding latency to the run time.
3. On slower CI runners or under load, the arbitrary 500ms may still be
insufficient or
unnecessary; the test still depends on explicit waitFor(...) earlier
(ThemeModal.test.tsx:518-524) and later (ThemeModal.test.tsx:538-545). The
timeout is not
tied to a deterministic DOM condition and can either waste time or produce
flakiness.
4. Removing the hard sleep and relying on explicit waitFor checks (as
suggested) yields
deterministic waits and reduces test duration and flakiness.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset-frontend/src/features/themes/ThemeModal.test.tsx
**Line:** 527:528
**Comment:**
*Possible Bug: Using a hard-coded 500ms timeout to "give extra time"
introduces unnecessary test delay and potential flakiness, since the test is
not tied to any specific condition and may still race on slower environments;
it is safer to rely solely on explicit waits for the relevant DOM state.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
##########
superset-frontend/src/theme/ThemeController.ts:
##########
@@ -551,18 +562,32 @@ export class ThemeController {
this.persistMode();
this.notifyListeners();
} catch (error) {
- console.error('Failed to update theme:', error);
- this.fallbackToDefaultMode();
+ await this.fallbackToDefaultMode();
}
}
/**
- * Fallback to default mode with error recovery.
+ * Fallback to default mode with runtime error recovery.
+ * Tries to fetch a fresh system default theme from the API.
*/
- private fallbackToDefaultMode(): void {
+ private async fallbackToDefaultMode(): Promise<void> {
this.currentMode = ThemeMode.DEFAULT;
- // Get the default theme which will have the correct algorithm
+ // Try to fetch fresh system default theme from server
+ const freshSystemTheme = await this.fetchSystemDefaultTheme();
+
+ if (freshSystemTheme) {
+ try {
+ await this.applyThemeWithRecovery(freshSystemTheme);
+ this.persistMode();
+ this.notifyListeners();
+ return;
+ } catch (error) {
+ // Fresh theme also failed, continue to final fallback
+ }
+ }
+
+ // Final fallback: use cached default theme or built-in theme
const defaultTheme: AnyThemeConfig =
this.getThemeForMode(ThemeMode.DEFAULT) || this.defaultTheme || {};
Review Comment:
**Suggestion:** The async `fallbackToDefaultMode` method is called without
`await`, and its final `applyTheme` call is not wrapped in a try/catch, so if
applying the fallback theme throws, the resulting rejected Promise will be
unhandled at the call sites, causing unhandled promise rejection errors in the
browser. [possible bug]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ❌ Unhandled promise rejections in browser console.
- ⚠️ Theme updates produce noisy client errors.
- ⚠️ UI flows (setTheme, setThemeMode) may appear flaky.
```
</details>
```suggestion
try {
this.applyTheme(defaultTheme);
} catch (error) {
console.error('Failed to apply fallback default theme:', error);
return;
}
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Call a public method that triggers theme application without awaiting it
— e.g.,
ThemeController.setTheme(...) which calls this.updateTheme(...) but does not
await the
Promise (setTheme is synchronous and invokes updateTheme directly).
2. Inside updateTheme, an error occurs during applyTheme(...) and the catch
handler runs
await this.fallbackToDefaultMode(); (see updateTheme catch invoking
fallbackToDefaultMode
at superset-frontend/src/theme/ThemeController.ts line ~565).
3. fallbackToDefaultMode is async; its final fallback calls
this.applyTheme(defaultTheme)
synchronously (lines 593-593). If applyTheme throws while
fallbackToDefaultMode is
executing, that exception rejects the fallbackToDefaultMode Promise.
4. Because callers like setTheme do not await updateTheme(), the rejected
Promise from
fallbackToDefaultMode / updateTheme becomes an unhandled promise rejection
in the browser
console. This manifests as uncaught errors and noisy logs during normal UI
flows (theme
set/reset operations).
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset-frontend/src/theme/ThemeController.ts
**Line:** 593:593
**Comment:**
*Possible Bug: The async `fallbackToDefaultMode` method is called
without `await`, and its final `applyTheme` call is not wrapped in a try/catch,
so if applying the fallback theme throws, the resulting rejected Promise will
be unhandled at the call sites, causing unhandled promise rejection errors in
the browser.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
##########
superset-frontend/src/features/themes/ThemeModal.test.tsx:
##########
@@ -37,6 +38,27 @@ jest.mock('src/dashboard/util/permissionUtils', () => ({
isUserAdmin: jest.fn(() => true),
}));
+// Mock JsonEditor to avoid direct DOM manipulation in tests
+jest.mock('@superset-ui/core/components/AsyncAceEditor', () => ({
+ ...jest.requireActual('@superset-ui/core/components/AsyncAceEditor'),
+ JsonEditor: ({
+ onChange,
+ value,
+ readOnly,
+ }: {
+ onChange: (value: string) => void;
+ value: string;
+ readOnly?: boolean;
+ }) => (
+ <textarea
+ data-test="json-editor"
Review Comment:
**Suggestion:** The mocked JsonEditor component sets a `data-test`
attribute, but the helper functions use `screen.getByTestId('json-editor')`,
which queries `data-testid`; this mismatch will cause all helpers using
`getByTestId` to throw because the element cannot be found. [possible bug]
<details>
<summary><b>Severity Level:</b> Critical 🚨</summary>
```mdx
- ❌ ThemeModal unit tests fail in CI.
- ❌ Local test runs error immediately locating editor.
- ⚠️ Blocks related theme modal test coverage verification.
```
</details>
```suggestion
data-testid="json-editor"
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Open the test file
superset-frontend/src/features/themes/ThemeModal.test.tsx and locate
the mocked JsonEditor definition at ThemeModal.test.tsx:44-59. The mock
returns a
<textarea data-test="json-editor"> (lines 53-55 in the PR diff).
2. Run the test suite (e.g., yarn test ThemeModal.test.tsx). Multiple helper
functions in
the same file call screen.getByTestId('json-editor') — see addValidJsonData
at
ThemeModal.test.tsx:116-125 which executes screen.getByTestId('json-editor')
at
ThemeModal.test.tsx:122.
3. When addValidJsonData (ThemeModal.test.tsx:116) or addJsonWithUnknownToken
(ThemeModal.test.tsx:128) calls screen.getByTestId('json-editor')
(ThemeModal.test.tsx:122
/ 135), testing-library will throw because no element with
data-testid="json-editor"
exists (the mock set data-test instead).
4. The test run fails immediately with a getByTestId error (element not
found). The fix is
to change the mock to use data-testid (as proposed) so screen.getByTestId
resolves to the
mocked textarea.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset-frontend/src/features/themes/ThemeModal.test.tsx
**Line:** 54:54
**Comment:**
*Possible Bug: The mocked JsonEditor component sets a `data-test`
attribute, but the helper functions use `screen.getByTestId('json-editor')`,
which queries `data-testid`; this mismatch will cause all helpers using
`getByTestId` to throw because the element cannot be found.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
##########
superset-frontend/src/theme/tests/ThemeController.test.ts:
##########
@@ -833,6 +833,206 @@ test('ThemeController handles theme application errors',
() => {
fallbackSpy.mockRestore();
});
+test('ThemeController constructor recovers from corrupted stored theme', () =>
{
+ // Simulate corrupted dev theme override in storage
+ const corruptedTheme = { token: { colorPrimary: '#ff0000' } };
+ mockLocalStorage.getItem.mockImplementation((key: string) => {
+ if (key === 'superset-dev-theme-override') {
+ return JSON.stringify(corruptedTheme);
+ }
+ return null;
+ });
+
+ // Mock Theme.fromConfig to return object with toSerializedConfig
+ mockThemeFromConfig.mockReturnValue({
+ ...mockThemeObject,
+ toSerializedConfig: () => corruptedTheme,
+ });
+
+ // First call throws (corrupted theme), second call succeeds (fallback)
+ let callCount = 0;
+ mockSetConfig.mockImplementation(() => {
+ callCount += 1;
+ if (callCount === 1) {
+ throw new Error('Invalid theme configuration');
+ }
+ });
+
+ const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
+
+ // Should not throw - constructor should recover
+ const controller = createController();
+
+ // Verify recovery happened
+ expect(consoleWarnSpy).toHaveBeenCalledWith(
+ 'Failed to apply stored theme, clearing invalid overrides:',
+ expect.any(Error),
+ );
+
+ // Verify invalid overrides were cleared from storage
+ expect(mockLocalStorage.removeItem).toHaveBeenCalledWith(
+ 'superset-dev-theme-override',
+ );
+ expect(mockLocalStorage.removeItem).toHaveBeenCalledWith(
+ 'superset-crud-theme-id',
+ );
+ expect(mockLocalStorage.removeItem).toHaveBeenCalledWith(
+ 'superset-applied-theme-id',
+ );
+
+ // Verify controller is in a valid state
+ expect(controller.getCurrentMode()).toBe(ThemeMode.DEFAULT);
+
+ consoleWarnSpy.mockRestore();
Review Comment:
**Suggestion:** This test creates a second spy on `console.warn` and then
restores it, which overrides and effectively disables the shared `consoleSpy`
spy defined at the top of the file for all subsequent tests; this can cause
later tests that rely on `consoleSpy` to silently stop capturing warnings or
assertions to fail unexpectedly. Use the existing `consoleSpy` instead of
creating and restoring a new spy within this test. [logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ❌ ThemeController.test.ts assertions may silently stop capturing warnings.
- ⚠️ CI runs for theme tests may become flaky or fail.
- ⚠️ Local developers get confusing test failures.
```
</details>
```suggestion
// Should not throw - constructor should recover
const controller = createController();
// Verify recovery happened
expect(consoleSpy).toHaveBeenCalledWith(
'Failed to apply stored theme, clearing invalid overrides:',
expect.any(Error),
);
// Verify invalid overrides were cleared from storage
expect(mockLocalStorage.removeItem).toHaveBeenCalledWith(
'superset-dev-theme-override',
);
expect(mockLocalStorage.removeItem).toHaveBeenCalledWith(
'superset-crud-theme-id',
);
expect(mockLocalStorage.removeItem).toHaveBeenCalledWith(
'superset-applied-theme-id',
);
// Verify controller is in a valid state
expect(controller.getCurrentMode()).toBe(ThemeMode.DEFAULT);
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. Open the test file at
superset-frontend/src/theme/tests/ThemeController.test.ts and
locate the shared spy definition created near the top of the file (the
shared spy is
declared as `const consoleSpy = jest.spyOn(console,
'warn').mockImplementation();` defined
immediately after the `createController` helper — see createController at
`.../ThemeController.test.ts:106-112` in the repository context). This
shared spy is
expected to capture all console.warn calls across tests.
2. Run the Jest test runner for this file (e.g., `yarn test
ThemeController.test.ts` or
`npm run test -- ThemeController.test.ts`). Jest will execute tests in file
order. When
execution reaches the test "ThemeController constructor recovers from
corrupted stored
theme" (the test block added between lines `836` and `887` in the PR), the
test executes
the code at `.../ThemeController.test.ts:861` which calls
`jest.spyOn(console, 'warn')`
again, creating a second spy (local variable `consoleWarnSpy`).
3. Later in that same test, the code calls `consoleWarnSpy.mockRestore()`
(line `886`).
mockRestore() restores console.warn to the original implementation, which
has the side
effect of removing the previously-installed shared spy (`consoleSpy`). This
happens while
other tests in the same file are still scheduled to run.
4. After this restoration, any subsequent tests that assert on or rely upon
the shared
`consoleSpy` (the spy created at file top) will not capture warnings. This
produces
missing spy calls and can cause downstream assertions to fail or tests to
appear flaky.
Replacing the local spy usage with assertions against the shared
`consoleSpy` (or avoiding
mockRestore here) prevents the shared spy from being disabled and reproduces
a passing
test run.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset-frontend/src/theme/tests/ThemeController.test.ts
**Line:** 861:886
**Comment:**
*Logic Error: This test creates a second spy on `console.warn` and then
restores it, which overrides and effectively disables the shared `consoleSpy`
spy defined at the top of the file for all subsequent tests; this can cause
later tests that rely on `consoleSpy` to silently stop capturing warnings or
assertions to fail unexpectedly. Use the existing `consoleSpy` instead of
creating and restoring a new spy within this test.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
##########
superset-frontend/src/theme/ThemeController.ts:
##########
@@ -551,18 +562,32 @@ export class ThemeController {
this.persistMode();
this.notifyListeners();
} catch (error) {
- console.error('Failed to update theme:', error);
- this.fallbackToDefaultMode();
+ await this.fallbackToDefaultMode();
}
}
/**
- * Fallback to default mode with error recovery.
+ * Fallback to default mode with runtime error recovery.
+ * Tries to fetch a fresh system default theme from the API.
*/
- private fallbackToDefaultMode(): void {
+ private async fallbackToDefaultMode(): Promise<void> {
this.currentMode = ThemeMode.DEFAULT;
- // Get the default theme which will have the correct algorithm
+ // Try to fetch fresh system default theme from server
+ const freshSystemTheme = await this.fetchSystemDefaultTheme();
+
+ if (freshSystemTheme) {
+ try {
+ await this.applyThemeWithRecovery(freshSystemTheme);
+ this.persistMode();
+ this.notifyListeners();
+ return;
+ } catch (error) {
+ // Fresh theme also failed, continue to final fallback
+ }
+ }
+
+ // Final fallback: use cached default theme or built-in theme
const defaultTheme: AnyThemeConfig =
this.getThemeForMode(ThemeMode.DEFAULT) || this.defaultTheme || {};
Review Comment:
**Suggestion:** In the final fallback path, using the mode-based resolver
can still return the development override instead of a clean default theme, so
if the dev override was the source of the error, the controller will reapply
the same broken theme instead of a safe default, defeating the recovery
mechanism. [logic error]
<details>
<summary><b>Severity Level:</b> Major ⚠️</summary>
```mdx
- ❌ Theme recovery may reapply corrupted dev overrides.
- ⚠️ ThemeModal preview shows broken themes after recovery.
- ⚠️ Admins cannot reliably reset to safe default themes.
```
</details>
```suggestion
const defaultTheme: AnyThemeConfig = this.defaultTheme || {};
```
<details>
<summary><b>Steps of Reproduction ✅ </b></summary>
```mdx
1. In the running app, create a corrupted development override by calling
ThemeController.setTemporaryTheme(...) which sets this.devThemeOverride and
persists it
(method present in superset-frontend/src/theme/ThemeController.ts; the
implementation sets
this.devThemeOverride and then calls this.updateTheme(...)). Observe a
broken theme is
stored in memory and storage.
2. Trigger a runtime failure when the app later tries to apply themes (for
example, an
invalid token causes applyTheme to throw inside ThemeController.applyTheme
at the apply
step — see applyTheme re-throw behavior around the applyTheme method in this
file).
3. updateTheme catches the error and calls fallbackToDefaultMode via await
this.fallbackToDefaultMode(); (see updateTheme catch at
superset-frontend/src/theme/ThemeController.ts line ~565 where
fallbackToDefaultMode is
invoked).
4. Inside fallbackToDefaultMode the "final fallback" resolves defaultTheme
using
this.getThemeForMode(ThemeMode.DEFAULT) (lines 591-592). Because
getThemeForMode gives
priority to this.devThemeOverride (it returns the dev override whenever
present), the
controller will reapply the same corrupted devThemeOverride instead of a
clean built-in
default, defeating recovery.
Note: This reproduces with normal usage of the ThemeController APIs
(setTemporaryTheme ->
later theme application error -> fallback flow). The root cause is
getThemeForMode
precedence combined with not clearing devThemeOverride before the final
fallback
selection.
```
</details>
<details>
<summary><b>Prompt for AI Agent 🤖 </b></summary>
```mdx
This is a comment left during a code review.
**Path:** superset-frontend/src/theme/ThemeController.ts
**Line:** 591:592
**Comment:**
*Logic Error: In the final fallback path, using the mode-based resolver
can still return the development override instead of a clean default theme, so
if the dev override was the source of the error, the controller will reapply
the same broken theme instead of a safe default, defeating the recovery
mechanism.
Validate the correctness of the flagged issue. If correct, How can I resolve
this? If you propose a fix, implement it and please make it concise.
```
</details>
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]