Skip to content

fix(dashboard): handle missing chart metadata in dashboard export - #40575

Open
mostafamos wants to merge 1 commit into
apache:masterfrom
mostafamos:devin/1780297176-fix-keyerror-dashboard-export
Open

fix(dashboard): handle missing chart metadata in dashboard export#40575
mostafamos wants to merge 1 commit into
apache:masterfrom
mostafamos:devin/1780297176-fix-keyerror-dashboard-export

Conversation

@mostafamos

Copy link
Copy Markdown

Guard against KeyError when position entries have missing or corrupt 'type', 'meta', or 'chartId' keys in build_uuid_to_id_map() and the update_id_refs() position loop. Also add id_map membership checks in timed_refresh_immune_slices and expanded_slices to drop stale IDs.

Closes #3

SUMMARY

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

TESTING INSTRUCTIONS

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@dosubot dosubot Bot added the dashboard:import Related to importing dashboards label Jun 1, 2026
@bito-code-review

bito-code-review Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #cd74d9

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 1fc969f..1fc969f
    • superset/commands/dashboard/importers/v1/utils.py
    • tests/unit_tests/dashboards/commands/importers/v1/utils_test.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +65 to +66
if uuid is not None and chart_id is not None:
result[uuid] = chart_id

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The new guard still accepts any non-None uuid, including unhashable values like dict/list from corrupt payloads. Writing result[uuid] will then raise TypeError and break dashboard import. Validate that uuid is a string (or at least hashable) before using it as a dict key. [type error]

Severity Level: Major ⚠️
- ❌ Assets import API crashes on malformed dashboard UUID meta.
- ⚠️ Dashboard bundles with corrupt chart UUIDs cannot be imported.
Steps of Reproduction ✅
1. Trigger the assets import API endpoint `POST /api/v1/assets/import/`, which is
implemented by `ImportExportRestApi.import_` in `superset/importexport/api.py:95-103`.
This endpoint reads the uploaded ZIP bundle and calls `ImportAssetsCommand(...)` at
`superset/importexport/api.py:228-237`.

2. Ensure the uploaded bundle contains a dashboard V1 YAML under `dashboards/` whose
`position` field includes a CHART node with `meta["uuid"]` set to a YAML mapping (parsed
as a Python `dict`) and `meta["chartId"]` set to a valid integer. The resulting config
dict is passed unchanged into `ImportAssetsCommand._import` in
`superset/commands/importers/v1/assets.py:122-165`.

3. In `ImportAssetsCommand._import`, for each `dashboards/...` file, the code executes
`config = update_id_refs(config, chart_ids, dataset_info)` at
`superset/commands/importers/v1/assets.py:161-165`. This calls `update_id_refs` in
`superset/commands/dashboard/importers/v1/utils.py:85-99` with the corrupted `position`
structure from the bundle.

4. Inside `update_id_refs`, `old_ids = build_uuid_to_id_map(fixed["position"])` at
`superset/commands/dashboard/importers/v1/utils.py:94` calls `build_uuid_to_id_map`. In
that function, the corrupted CHART node is processed, `uuid` is a `dict`, `chart_id` is an
`int`, the condition at line 65 (`if uuid is not None and chart_id is not None:`) passes,
and line 66 (`result[uuid] = chart_id`) raises `TypeError: unhashable type: 'dict'`,
causing the entire import request to fail with a server error instead of skipping the
malformed entry.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/commands/dashboard/importers/v1/utils.py
**Line:** 65:66
**Comment:**
	*Type Error: The new guard still accepts any non-`None` `uuid`, including unhashable values like dict/list from corrupt payloads. Writing `result[uuid]` will then raise `TypeError` and break dashboard import. Validate that `uuid` is a string (or at least hashable) before using it as a dict key.

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.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

metadata["expanded_slices"] = {
str(id_map[int(old_id)]): value
for old_id, value in metadata["expanded_slices"].items()
if int(old_id) in id_map

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Converting old_id with int(old_id) in the filter condition will throw ValueError for malformed keys (for example "foo"), so corrupt expanded_slices data still crashes import instead of being skipped. Parse IDs defensively (try/except) and drop non-numeric entries. [type error]

Severity Level: Major ⚠️
- ❌ Assets import API crashes on malformed expanded_slices metadata.
- ⚠️ Dashboards with corrupt expanded_slices JSON cannot be imported.
Steps of Reproduction ✅
1. Use the assets import REST endpoint `POST /api/v1/assets/import/` implemented by
`ImportExportRestApi.import_` in `superset/importexport/api.py:95-103`. This endpoint
unwraps the uploaded ZIP and constructs an `ImportAssetsCommand` at
`superset/importexport/api.py:228-237` with the bundle contents.

2. Ensure the bundle includes a `dashboards/...` YAML whose `metadata` JSON (stored under
`json_metadata`) contains an `expanded_slices` object with at least one key that is not a
numeric string, for example `{"foo": true}`. When loaded, this becomes
`metadata["expanded_slices"]` in the `config` dict consumed by
`ImportAssetsCommand._import` in
`superset/commands/importers/v1/assets.py:134-143,161-165`.

3. In `ImportAssetsCommand._import`, for each dashboard file, the code calls `config =
update_id_refs(config, chart_ids, dataset_info)` at
`superset/commands/importers/v1/assets.py:161-165`, invoking `update_id_refs` in
`superset/commands/dashboard/importers/v1/utils.py:85-99` with the corrupted `metadata`
dict and the generated `id_map`.

4. Inside `update_id_refs`, the `"expanded_slices"` block at
`superset/commands/dashboard/importers/v1/utils.py:126-131` executes when that key is
present. During the dict comprehension, for the non-numeric key `old_id="foo"`, the filter
condition `if int(old_id) in id_map` at line 130 calls `int("foo")` and raises
`ValueError: invalid literal for int() with base 10: 'foo'`. This exception is uncaught,
causing the entire dashboard import to fail instead of simply dropping the malformed
`expanded_slices` entry.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/commands/dashboard/importers/v1/utils.py
**Line:** 130:130
**Comment:**
	*Type Error: Converting `old_id` with `int(old_id)` in the filter condition will throw `ValueError` for malformed keys (for example `"foo"`), so corrupt `expanded_slices` data still crashes import instead of being skipped. Parse IDs defensively (try/except) and drop non-numeric entries.

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.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +153 to +154
if "uuid" in meta and meta["uuid"] in chart_ids:
meta["chartId"] = chart_ids[meta["uuid"]]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This membership check can raise TypeError when meta["uuid"] is an unhashable corrupt value (for example, a dict/list), so the "defensive" path still crashes on malformed exports. Add a type/hashability check before probing chart_ids. [type error]

Severity Level: Major ⚠️
- ❌ Assets import API crashes remapping chart IDs on import.
- ⚠️ Dashboard imports with corrupt UUID metadata fail unpredictably.
Steps of Reproduction ✅
1. Call the assets import endpoint `POST /api/v1/assets/import/` handled by
`ImportExportRestApi.import_` in `superset/importexport/api.py:95-103`, which constructs
an `ImportAssetsCommand` at `superset/importexport/api.py:228-237` from the uploaded ZIP
bundle.

2. Include in the bundle a `dashboards/...` YAML whose `position` field has a CHART entry
with `meta["uuid"]` set to a YAML mapping (Python `dict`) and `meta["chartId"]` either
missing or explicitly `null`. This configuration is loaded into the `config` dict passed
to `ImportAssetsCommand._import` in `superset/commands/importers/v1/assets.py:122-165`.

3. In `ImportAssetsCommand._import`, when processing `dashboards/...` entries, the code
calls `config = update_id_refs(config, chart_ids, dataset_info)` at
`superset/commands/importers/v1/assets.py:161-165`, invoking `update_id_refs` in
`superset/commands/dashboard/importers/v1/utils.py:85-99` with the corrupted `position`.

4. Inside `update_id_refs`, after metadata fixes, the "fix position" loop at
`superset/commands/dashboard/importers/v1/utils.py:143-155` iterates over
`position.values()`. For the corrupted CHART child, `meta` is a dict and the condition `if
"uuid" in meta and meta["uuid"] in chart_ids:` at line 153 executes. Since `meta["uuid"]`
is a `dict`, evaluating `meta["uuid"] in chart_ids` raises `TypeError: unhashable type:
'dict'` before the body runs, crashing the import flow even though earlier guards were
intended to make the code more defensive.

Fix in Cursor | Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/commands/dashboard/importers/v1/utils.py
**Line:** 153:154
**Comment:**
	*Type Error: This membership check can raise `TypeError` when `meta["uuid"]` is an unhashable corrupt value (for example, a dict/list), so the "defensive" path still crashes on malformed exports. Add a type/hashability check before probing `chart_ids`.

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.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@codecov

codecov Bot commented Jun 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.91304% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.44%. Comparing base (6f12d17) to head (101b04e).
⚠️ Report is 1291 commits behind head on master.

Files with missing lines Patch % Lines
superset/commands/dashboard/importers/v1/utils.py 73.91% 2 Missing and 4 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master   #40575   +/-   ##
=======================================
  Coverage   64.44%   64.44%           
=======================================
  Files        2655     2655           
  Lines      145473   145493   +20     
  Branches    33575    33583    +8     
=======================================
+ Hits        93747    93761   +14     
- Misses      50027    50029    +2     
- Partials     1699     1703    +4     
Flag Coverage Δ
hive 39.22% <0.00%> (-0.02%) ⬇️
mysql 58.00% <73.91%> (+<0.01%) ⬆️
postgres 58.07% <73.91%> (+<0.01%) ⬆️
presto 40.80% <0.00%> (-0.02%) ⬇️
python 59.50% <73.91%> (+<0.01%) ⬆️
sqlite 57.72% <73.91%> (+<0.01%) ⬆️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@rusackas

Copy link
Copy Markdown
Member

This needs a rebase on current master before it can go anywhere - GitHub shows it as conflicting.

Couple of things while you're in here: the title says "export" but everything's in importers/v1/utils.py, so this is really an import fix, right? And "Closes #3" doesn't look like a real reference - is there an actual issue or repro for the corrupt-position scenario this guards against?

Mostly trying to understand where these malformed position payloads come from, since if they're hitting import we may want to know rather than silently dropping timed_refresh_immune_slices/expanded_slices entries.

Guard against KeyError when position entries have missing or corrupt
'type', 'meta', or 'chartId' keys in build_uuid_to_id_map() and the
update_id_refs() position loop. Also add id_map membership checks in
timed_refresh_immune_slices and expanded_slices to drop stale IDs.

Closes #3

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@rusackas
rusackas force-pushed the devin/1780297176-fix-keyerror-dashboard-export branch from 1fc969f to 101b04e Compare June 24, 2026 18:54
@bito-code-review

bito-code-review Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #a085c6

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 101b04e..101b04e
    • superset/commands/dashboard/importers/v1/utils.py
    • tests/unit_tests/dashboards/commands/importers/v1/utils_test.py
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@rusackas

rusackas commented Jul 7, 2026

Copy link
Copy Markdown
Member

Would love updates on the various review comments (bot and human) if you want to move this forward. Thanks in advance :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dashboard:import Related to importing dashboards size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants