Skip to content

fix(mcp): ASCII chart crashes with NaN when dataset contains null values#39916

Open
aminghadersohi wants to merge 7 commits intoapache:masterfrom
aminghadersohi:fix/mcp-ascii-chart-nan
Open

fix(mcp): ASCII chart crashes with NaN when dataset contains null values#39916
aminghadersohi wants to merge 7 commits intoapache:masterfrom
aminghadersohi:fix/mcp-ascii-chart-nan

Conversation

@aminghadersohi
Copy link
Copy Markdown
Contributor

SUMMARY

When a dataset contains NULL values, they are represented as float('nan') after numeric conversion. The ASCII bar chart renderers passed NaN directly into int(), raising ValueError: cannot convert float NaN to integer.

Root cause: _generate_ascii_bar_chart and _extract_time_series_data accepted float('nan') values through the isinstance(val, (int, float)) check, which returns True for NaN. The NaN then propagated into max() / normalization calculations, causing the crash at int(normalized * max_bar_width).

Three-layer fix:

  1. Extraction-time filtering_generate_ascii_bar_chart and _extract_time_series_data now call _is_nan_value() (already defined in the module) before accepting a numeric value, silently skipping NaN rows.
  2. Defence-in-depth guards_generate_horizontal_bar_chart and _generate_vertical_bar_chart check _is_nan_value(normalized) before the int() call, treating NaN bar size as 0 (no bar drawn).

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — ASCII chart output; crash prevented.

TESTING INSTRUCTIONS

  1. Create a dataset with at least one NULL value in a numeric column.
  2. Render a bar or line chart via the MCP get_chart_preview tool.
  3. Before this fix: ValueError: cannot convert float NaN to integer.
  4. After this fix: chart renders with NaN rows silently skipped.

Unit tests added in tests/unit_tests/mcp_service/chart/test_ascii_charts.py covering bar, column, line, and timeseries bar chart types with NaN and None values.

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
  • Introduces new feature or API
  • Removes existing feature or API

When a dataset contains NULL values they become float NaN after numeric
conversion.  The horizontal and vertical bar chart renderers passed NaN
into int(), raising ValueError.  Likewise the line chart extractor would
include NaN in the value list, corrupting min/max calculations.

Three-layer fix:
1. Filter NaN during value extraction in _generate_ascii_bar_chart and
   _extract_time_series_data so NaN rows are silently skipped.
2. Add _is_nan_value() guard before int() in _generate_horizontal_bar_chart
   (bar_length) and _generate_vertical_bar_chart (bar_height) as
   defence-in-depth; NaN bar size is treated as 0 (no bar drawn).

Fixes: ValueError: cannot convert float NaN to integer
@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

❌ Patch coverage is 0% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.87%. Comparing base (5b5dd01) to head (a560f84).
⚠️ Report is 7 commits behind head on master.

Files with missing lines Patch % Lines
superset/mcp_service/chart/ascii_charts.py 0.00% 9 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #39916      +/-   ##
==========================================
- Coverage   63.88%   63.87%   -0.02%     
==========================================
  Files        2583     2583              
  Lines      136604   136629      +25     
  Branches    31502    31506       +4     
==========================================
  Hits        87276    87276              
- Misses      47812    47837      +25     
  Partials     1516     1516              
Flag Coverage Δ
hive 39.37% <0.00%> (-0.02%) ⬇️
mysql 59.03% <0.00%> (-0.03%) ⬇️
postgres 59.11% <0.00%> (-0.03%) ⬇️
presto 41.06% <0.00%> (-0.02%) ⬇️
python 60.55% <0.00%> (-0.03%) ⬇️
sqlite 58.75% <0.00%> (-0.03%) ⬇️
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.

Vertical bar chart (chosen when avg label length <= 8) truncates labels
to 3 characters, so 'Alpha' never appears literally. Use longer labels
(> 8 chars average) to force horizontal layout, where the full label is
preserved up to 15 characters.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes MCP ASCII chart preview rendering crashes when datasets contain NULLs that become NaN during numeric conversion, by filtering NaN values during data extraction and guarding the int() bar-size conversion paths. Adds unit tests to exercise bar/column/line/timeseries behavior with NaN and None inputs.

Changes:

  • Filter out NaN numeric values during bar and time-series data extraction.
  • Add defense-in-depth checks to avoid int(float('nan')) when computing bar sizes.
  • Add unit tests covering chart generation with NaN/None values.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
superset/mcp_service/chart/ascii_charts.py Filters NaN during extraction and adds guards before converting normalized values to int() to prevent crashes.
tests/unit_tests/mcp_service/chart/test_ascii_charts.py Adds unit tests for NaN/None handling across several ASCII chart types.

Comment thread tests/unit_tests/mcp_service/chart/test_ascii_charts.py
Comment thread tests/unit_tests/mcp_service/chart/test_ascii_charts.py Outdated
Assert the exact fallback message 'No numeric data found for bar chart'
instead of just checking len(result) > 0, which would pass even on
unrelated error strings.
@aminghadersohi aminghadersohi marked this pull request as ready for review May 7, 2026 02:37
@dosubot dosubot Bot added the viz:charts:bar Related to the Bar chart label May 7, 2026
- test_horizontal_bar_chart_nan_rows_are_skipped: replace weak `or`
  assertion with separate `assert "Alpha" in result`, `assert "Gamma"
  in result`, and `assert "Beta" not in result`, making the test
  deterministic and verifying the NaN row is actually excluded

- test_bar_chart_with_all_null_values_returns_fallback: add
  `isinstance(result, str)` and `assert "█" not in result` to
  explicitly verify no bar content is rendered in the fallback path
Add `assert "Horizontal Bar Chart" in result` to make it explicit that
the horizontal renderer is chosen (avg label length 14 > 8 threshold).
Horizontal mode preserves full label text; vertical would truncate to
3 chars, invalidating the "Alpha"/"Gamma" presence assertions.
Comment thread superset/mcp_service/chart/ascii_charts.py
Comment thread superset/mcp_service/chart/ascii_charts.py
…derers

bool is a subclass of int, so isinstance(True, (int, float)) returns True.
Without an explicit bool guard the extractor would lock onto a boolean
column (is_active, flag, etc.) and ignore the real numeric metric,
producing 0/1 bars instead of actual values.

Add not isinstance(val, bool) to the numeric guard in both
_generate_ascii_bar_chart and _extract_time_series_data.
@bito-code-review
Copy link
Copy Markdown
Contributor

bito-code-review Bot commented May 7, 2026

Code Review Agent Run #f8215a

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 393906c..f80e3b3
    • superset/mcp_service/chart/ascii_charts.py
    • tests/unit_tests/mcp_service/chart/test_ascii_charts.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

…trengthen tests

- Replace old `val != val` NaN idiom and add bool guard in _extract_scatter_data
  to match the pattern used in bar/line extractors
- Add scatter chart NaN and boolean test coverage
- Remove redundant bar fallback assertion; strengthen bool-skip negative check
@netlify
Copy link
Copy Markdown

netlify Bot commented May 7, 2026

Deploy Preview for superset-docs-preview ready!

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

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

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

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

Labels

size/L viz:charts:bar Related to the Bar chart

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants