From 2a1f632daa935dad93b3a39e080b88b9d92b6fb2 Mon Sep 17 00:00:00 2001 From: Evan Rusackas Date: Tue, 30 Jun 2026 16:10:01 -0700 Subject: [PATCH] fix(dashboard): surface size, limit, and config key in oversized dashboard error (#41532) Co-authored-by: Claude Opus 4.8 --- .../configuration/configuring-superset.mdx | 18 ++++++++++++ .../components/Header/Header.test.tsx | 28 +++++++++++++++++++ .../src/dashboard/components/Header/index.tsx | 4 ++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/docs/admin_docs/configuration/configuring-superset.mdx b/docs/admin_docs/configuration/configuring-superset.mdx index 9ef6f075022..1ae2e1ba50b 100644 --- a/docs/admin_docs/configuration/configuring-superset.mdx +++ b/docs/admin_docs/configuration/configuring-superset.mdx @@ -549,6 +549,24 @@ CELERY_BEAT_SCHEDULE = { Adjust `retention_period_days` to control how long query rows are kept. Companion opt-in tasks (`prune_logs`, `prune_tasks`) exist for pruning the logs and tasks tables; see the commented-out examples in `superset/config.py`. Without enabling these tasks, the metadata database will grow unbounded over time. +## Dashboard Layout Size Limit + +Each dashboard stores its layout (the position, size, and nesting of every chart, row, and tab) as a JSON blob in the metadata database. Superset caps the length of this serialized blob with `SUPERSET_DASHBOARD_POSITION_DATA_LIMIT`, which defaults to `65535`: + +```python +SUPERSET_DASHBOARD_POSITION_DATA_LIMIT = 65535 +``` + +This is a Python-level cap (65535 is 2¹⁶ − 1), independent of the database column capacity — the `position_json` column is a `MEDIUMTEXT`, which holds far more. When the serialized layout reaches this limit, the editor blocks the save and reports the current length, the limit, and this setting's name. A warning is shown once the layout passes 90% of the limit. + +Large dashboards — for example, many charts spread across nested tabs — can exceed the default. Because the underlying column comfortably stores larger values, you can safely raise the limit: + +```python +SUPERSET_DASHBOARD_POSITION_DATA_LIMIT = 131072 # double the default +``` + +Alternatively, split a very large dashboard into several smaller ones. Note that this check is enforced when saving layout edits in the UI; a dashboard imported from a ZIP with an oversized layout will load and render, but cannot be edited and re-saved until the limit is raised. + :::resources - [Blog: Feature Flags in Apache Superset](https://preset.io/blog/feature-flags-in-apache-superset-and-preset/) ::: diff --git a/superset-frontend/src/dashboard/components/Header/Header.test.tsx b/superset-frontend/src/dashboard/components/Header/Header.test.tsx index df0a4bc8799..78d6dd71f76 100644 --- a/superset-frontend/src/dashboard/components/Header/Header.test.tsx +++ b/superset-frontend/src/dashboard/components/Header/Header.test.tsx @@ -547,6 +547,34 @@ test('should save', () => { expect(onSave).toHaveBeenCalledTimes(1); }); +test('should block saving and surface the size, limit, and config key when the layout exceeds the limit', () => { + const oversizedState = { + ...editableState, + dashboardState: { + ...editableState.dashboardState, + hasUnsavedChanges: true, + }, + dashboardInfo: { + ...editableState.dashboardInfo, + common: { + conf: { + ...editableState.dashboardInfo.common.conf, + // any non-empty layout serializes to more than 1 character + SUPERSET_DASHBOARD_POSITION_DATA_LIMIT: 1, + }, + }, + }, + }; + setup(oversizedState); + userEvent.click(screen.getByText('Save')); + expect(onSave).not.toHaveBeenCalled(); + expect(addDangerToast).toHaveBeenCalledTimes(1); + const message = addDangerToast.mock.calls[0][0]; + expect(message).toContain('too large to save'); + expect(message).toContain('the limit is 1'); + expect(message).toContain('SUPERSET_DASHBOARD_POSITION_DATA_LIMIT'); +}); + test('should NOT render the "Draft" status', () => { const publishedState = { ...initialState, diff --git a/superset-frontend/src/dashboard/components/Header/index.tsx b/superset-frontend/src/dashboard/components/Header/index.tsx index 6131b5b31ab..1f0e4ff8982 100644 --- a/superset-frontend/src/dashboard/components/Header/index.tsx +++ b/superset-frontend/src/dashboard/components/Header/index.tsx @@ -468,7 +468,9 @@ const Header = (): JSX.Element => { if (positionJSONLength >= limit) { boundActionCreators.addDangerToast( t( - 'Your dashboard is too large. Please reduce its size before saving it.', + 'Your dashboard is too large to save: the serialized layout length is %s but the limit is %s. Reduce the dashboard size (for example, split it into multiple dashboards) or raise the SUPERSET_DASHBOARD_POSITION_DATA_LIMIT config setting.', + positionJSONLength.toLocaleString(), + limit.toLocaleString(), ), ); } else {