Skip to content

Explain figure rendering failures caused by kernel subshells - #597

Open
SimonHeybrock wants to merge 1 commit into
mainfrom
596-explain-subshell-concurrency
Open

Explain figure rendering failures caused by kernel subshells#597
SimonHeybrock wants to merge 1 commit into
mainfrom
596-explain-subshell-concurrency

Conversation

@SimonHeybrock

@SimonHeybrock SimonHeybrock commented Jul 31, 2026

Copy link
Copy Markdown
Member

Fixes #596 (as far as plopp can), and gives #499 a user-visible answer.

JupyterLab >= 4.4 defaults to routing widget comm messages over kernel subshells, which ipykernel >= 7 services on their own threads. ipympl canvas draw/resize requests are then handled concurrently with cell execution, and Matplotlib is not thread-safe: a live canvas racing a figure build corrupts the global mathtext parser state. Users see ParseException: Expected end of text, found '$' with a traceback that points at plopp and suggests nothing actionable. See ipympl#610 for the full analysis; the symptom also appears as blank canvases and kernel crashes.

There is no fix on our side -- a lock around our own calls does not cover ipympl's frame production and resize handlers. What we can do is stop the error from being a dead end, so this chains it into one that names the cause and the two verified workarounds (commsOverSubshells: disabled, or ipykernel<7), and documents them next to the installation instructions.

The message is only substituted when the kernel actually reports active subshells, so nothing changes for other rendering failures. Deliberately reactive rather than a warning at figure creation: with JupyterLab >= 4.4 and ipykernel >= 7 becoming the default, a proactive warning would fire in nearly every session, whether or not the race ever bites.

Test plan

  • Unit tests cover subshell detection against a stub kernel, and both branches of to_widget (explanatory error when subshells are active, original error re-raised otherwise).
  • Manual check that the message reads well in JupyterLab.

JupyterLab >= 4.4 routes widget messages over kernel subshells, which
ipykernel >= 7 services on their own threads. Drawing a live canvas then
races with figure creation during cell execution, and Matplotlib is not
thread-safe. The resulting mathtext parse error gives users no hint of
the cause or of the available workarounds.

When laying out a canvas fails while subshells are active, chain the
original error into one that names the cause and both workarounds, and
document them alongside the installation instructions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@nvaytet

nvaytet commented Jul 31, 2026

Copy link
Copy Markdown
Member

See also matplotlib/ipympl#621

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have my reservations as to how useful these tests are. We are trying to make a workaround for an issue that we hope will be fixed upstream.

Creating these fakes that mimic some private or undocumented properties of ipykernel feels like they could get out of date without us noticing.
We are also faking the error from the canvas, so the tests seem quite remote from the actual behaviour?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Should we try monkey-patching as in the PR you linked instead?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Matplotlib math text parsing error with Python 3.13 when using plopp.slicer

2 participants