Skip to content

fix(dashboard): escape emoji in position_json before saving to prevent truncation#39737

Merged
michael-s-molina merged 5 commits into
apache:masterfrom
justinpark:fix--emoji-pos-json-truncated
Apr 29, 2026
Merged

fix(dashboard): escape emoji in position_json before saving to prevent truncation#39737
michael-s-molina merged 5 commits into
apache:masterfrom
justinpark:fix--emoji-pos-json-truncated

Conversation

@justinpark
Copy link
Copy Markdown
Member

@justinpark justinpark commented Apr 29, 2026

SUMMARY

MySQL's utf8 charset only supports up to 3-byte UTF-8 characters. Emoji (e.g. 📈) are 4-byte Unicode characters, so when position_json containing raw emoji bytes is written directly to the DB column, MySQL silently truncates the string at the emoji character — breaking the stored JSON and causing parse errors on reload.

Root cause(introduced from https://github.com/apache/superset/pull/36332/changes#diff-a1256efcca3d8954c815c26f94d13eb86abd3f52c67258071fed1b3ece4a5b00R440-R446) : The PUT /api/v1/dashboard/ endpoint stores position_json via DashboardDAO.update() → setattr(model, "position_json", value), passing the raw string directly without re-serialization. This means any 4-byte emoji sent by the client reaches the DB as-is.

By contrast, the json_metadata.positions path (used when saving via set_dash_metadata) was unaffected because it always passes through json.dumps(), which uses simplejson with ensure_ascii=True by default — escaping emoji to \uXXXX (ASCII-safe).

Fix: In UpdateDashboardCommand.run(), parse and re-serialize position_json through json.loads → json.dumps before passing it to DashboardDAO.update(). This ensures emoji are escaped to \uXXXX and safe for MySQL utf8 columns.

if position_json := self._properties.get("position_json"):
self._properties["position_json"] = json.dumps(json.loads(position_json))

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

Screenshot 2026-04-28 at 5 03 29 PM

TESTING INSTRUCTIONS

  1. Create a dashboard with a Markdown component containing an emoji (e.g. 📈 See Tab)
  2. Edit one of the chart in the dashboard
  3. edited chart - save as new chart
  4. Adding to same dashboard same tab
  5. Reload the dashboard and verify the Markdown content is intact

or

  • Run the new integration tests:
    pytest tests/integration_tests/dashboards/test_update_emoji.py -v

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

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Apr 29, 2026

Code Review Agent Run #4ccbd8

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/integration_tests/dashboards/test_update_emoji.py - 1
Review Details
  • Files reviewed - 2 · Commit Range: 5dc5bed..5dc5bed
    • superset/commands/dashboard/update.py
    • tests/integration_tests/dashboards/test_update_emoji.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

@dosubot dosubot Bot added change:backend Requires changing the backend dashboard Namespace | Anything related to the Dashboard labels Apr 29, 2026
Comment thread superset/commands/dashboard/update.py Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.49%. Comparing base (4b42f82) to head (58890a3).
⚠️ Report is 7 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #39737   +/-   ##
=======================================
  Coverage   64.49%   64.49%           
=======================================
  Files        2566     2566           
  Lines      133960   133962    +2     
  Branches    31116    31117    +1     
=======================================
+ Hits        86394    86396    +2     
  Misses      46071    46071           
  Partials     1495     1495           
Flag Coverage Δ
hive 39.74% <0.00%> (-0.01%) ⬇️
mysql 60.19% <100.00%> (+<0.01%) ⬆️
postgres 60.27% <100.00%> (+<0.01%) ⬆️
presto 41.51% <0.00%> (-0.01%) ⬇️
python 61.83% <100.00%> (+<0.01%) ⬆️
sqlite 59.90% <100.00%> (+<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 Sentry.
📢 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.

@bito-code-review
Copy link
Copy Markdown
Contributor

The flagged issue is correct: the PR only adds emoji escaping for position_json in UpdateDashboardCommand.run, but the create path via POST /api/v1/dashboard lacks this normalization, risking MySQL utf8 truncation and broken JSON.

To resolve, apply the same json.loads → json.dumps normalization in CreateDashboardCommand.run before calling DashboardDAO.create.

superset/commands/dashboard/create.py

if position_json := self._properties.get("position_json"):
            self._properties["position_json"] = json.dumps(
                json.loads(position_json)
            )

        dashboard = DashboardDAO.create(self._properties)

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 29, 2026

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit d538ee9
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/69f158e816d43d0008208e11
😎 Deploy Preview https://deploy-preview-39737--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@justinpark
Copy link
Copy Markdown
Member Author

The flagged issue is correct: the PR only adds emoji escaping for position_json in UpdateDashboardCommand.run, but the create path via POST /api/v1/dashboard lacks this normalization, risking MySQL utf8 truncation and broken JSON.

To resolve, apply the same json.loads → json.dumps normalization in CreateDashboardCommand.run before calling DashboardDAO.create.

superset/commands/dashboard/create.py

if position_json := self._properties.get("position_json"):
            self._properties["position_json"] = json.dumps(
                json.loads(position_json)
            )

        dashboard = DashboardDAO.create(self._properties)

For creation, the positions value is already nested inside json_metadata and goes through the same dump process, so it's not needed

@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented Apr 29, 2026

Code Review Agent Run #929ad1

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 5dc5bed..d538ee9
    • tests/integration_tests/dashboards/test_update_emoji.py
    • superset/commands/dashboard/update.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

@michael-s-molina
Copy link
Copy Markdown
Member

@justinpark PR #36332 is a red herring in the bug report. It introduced the specific frontend behavior that triggered the code path more visibly (saving a chart to a dashboard tab), but the raw-setattr path in DashboardDAO.update() has
always been there.

@michael-s-molina michael-s-molina merged commit 54f1e32 into apache:master Apr 29, 2026
65 checks passed
michael-s-molina added a commit that referenced this pull request Apr 29, 2026
…t truncation (#39737)

Co-authored-by: Michael S. Molina <michael.s.molina@gmail.com>
(cherry picked from commit 54f1e32)
@bito-code-review
Copy link
Copy Markdown
Contributor

Bito Automatic Review Skipped – PR Already Merged

Bito scheduled an automatic review for this pull request, but the review was skipped because this PR was merged before the review could be run.
No action is needed if you didn't intend to review it. To get a review, you can type /review in a comment and save it

bestlong pushed a commit to bestlong/superset that referenced this pull request May 7, 2026
…t truncation (apache#39737)

Co-authored-by: Michael S. Molina <michael.s.molina@gmail.com>
qfcwell pushed a commit to qfcwell/superset that referenced this pull request May 12, 2026
…t truncation (apache#39737)

Co-authored-by: Michael S. Molina <michael.s.molina@gmail.com>
@github-actions github-actions Bot added 🍒 6.1.0 Cherry-picked to 6.1.0 🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels labels May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels change:backend Requires changing the backend dashboard Namespace | Anything related to the Dashboard size/L 🍒 6.1.0 Cherry-picked to 6.1.0

Projects

Development

Successfully merging this pull request may close these issues.

2 participants