From f9f0bce2f6862fc7a1e11a1c331a82fe2d1ff641 Mon Sep 17 00:00:00 2001 From: Eugen Goebel Date: Sat, 13 Jun 2026 19:42:45 +0200 Subject: [PATCH 1/2] Raise a clear error for unsupported marginal plot types Closes #4654. Passing an unsupported value to marginal_x, marginal_y or marginal in Plotly Express (for example marginal_x="density") left trace_spec as None in make_trace_spec, which later failed deep inside make_figure with a confusing "'NoneType' object has no attribute 'constructor'". make_trace_spec now raises a ValueError naming the offending value and listing the supported marginal plot types: rug, box, violin and histogram. The singular `marginal` argument is normalised to marginal_x or marginal_y before this point, so the single check covers all three parameters. Adds a regression test in tests/test_optional/test_px/test_marginals.py. --- CHANGELOG.md | 3 +++ plotly/express/_core.py | 6 ++++++ tests/test_optional/test_px/test_marginals.py | 12 ++++++++++++ 3 files changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f281cbf453a..d42062b5c48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +### Fixed +- Raise a clear `ValueError` when an unsupported marginal plot type is passed to Plotly Express, instead of failing later with a cryptic `'NoneType' object has no attribute 'constructor'` message [[#4654](https://github.com/plotly/plotly.py/issues/4654)] + ## [6.8.0] - 2026-06-03 diff --git a/plotly/express/_core.py b/plotly/express/_core.py index bcd5a40c031..3b8ff3e693e 100644 --- a/plotly/express/_core.py +++ b/plotly/express/_core.py @@ -971,6 +971,12 @@ def make_trace_spec(args, constructor, attrs, trace_patch): ), marginal=letter, ) + else: + raise ValueError( + "Invalid value '%s' for `marginal_%s`. Supported marginal " + "plot types are: 'rug', 'box', 'violin', 'histogram'." + % (args["marginal_" + letter], letter) + ) if "color" in attrs or "color" not in args: if "marker" not in trace_spec.trace_patch: trace_spec.trace_patch["marker"] = dict() diff --git a/tests/test_optional/test_px/test_marginals.py b/tests/test_optional/test_px/test_marginals.py index 9a7ec64d123..40d3019f2d3 100644 --- a/tests/test_optional/test_px/test_marginals.py +++ b/tests/test_optional/test_px/test_marginals.py @@ -24,3 +24,15 @@ def test_single_marginals(backend, px_fn, marginal, orientation): df, x="total_bill", y="total_bill", marginal=marginal, orientation=orientation ) assert len(fig.data) == 1 + (marginal is not None) + + +def test_unsupported_marginal_raises_clear_error(): # issue 4654 + # An unsupported marginal type used to fail deep inside make_figure with a + # cryptic "'NoneType' object has no attribute 'constructor'". It should + # instead raise a clear error naming the supported values. + with pytest.raises(ValueError, match="Supported marginal plot types"): + px.scatter(x=[1, 2, 3], y=[2, 3, 4], marginal_x="density") + with pytest.raises(ValueError, match="Supported marginal plot types"): + px.scatter(x=[1, 2, 3], y=[2, 3, 4], marginal_y="density") + with pytest.raises(ValueError, match="Supported marginal plot types"): + px.histogram(x=[1, 2, 3], marginal="density") From 0c27cf177e5cc34b1375f41e783286481d3479b9 Mon Sep 17 00:00:00 2001 From: Eugen Goebel Date: Sat, 13 Jun 2026 19:45:31 +0200 Subject: [PATCH 2/2] Point changelog entry at the PR instead of the issue --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d42062b5c48..3bbad0d2c08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased ### Fixed -- Raise a clear `ValueError` when an unsupported marginal plot type is passed to Plotly Express, instead of failing later with a cryptic `'NoneType' object has no attribute 'constructor'` message [[#4654](https://github.com/plotly/plotly.py/issues/4654)] +- Raise a clear `ValueError` when an unsupported marginal plot type is passed to Plotly Express, instead of failing later with a cryptic `'NoneType' object has no attribute 'constructor'` message [[#5625](https://github.com/plotly/plotly.py/pull/5625)] ## [6.8.0] - 2026-06-03