Repository navigation
"Resorting to unclean kill browser." when using fig.write_image() #430
Description
Activity
In another instance the following error message was displayed, the code was the same but I was not able to have it appear again:
ERROR:tornado.general:Uncaught exception in ZMQStream callback
Traceback (most recent call last):
File "~/.venv/lib/python3.13/site-packages/traitlets/traitlets.py", line 632, in get
value = obj._trait_values[self.name]
~~~~~~~~~~~~~~~~~^^^^^^^^^^^
KeyError: '_control_lock'During handling of the above exception, another exception occurred:
File "~/.venv/lib/python3.13/site-packages/traitlets/traitlets.py", line 831, in error
raise TraitError(e)
traitlets.traitlets.TraitError: The '_control_lock' trait of an IPythonKernel instance expected a Lock, not the NoneType None.
Resorting to unclean kill browser.The exact same behavior is occurring for me:
Using:
Windows
pip for package management
Python Python 3.11.5
Kaleido 1.3.0
nbformat not installed
choreographer 1.3.0
plotly 6.6.0even if you manually pin chrome version
import plotly.graph_objects as go import plotly.io as pio import os chrome_path = pio.get_chrome() print(chrome_path) os.environ["BROWSER_PATH"] = str(chrome_path) import plotly.graph_objects as go fig = go.Figure(go.Scatter(x=[1, 2, 3], y=[1, 3, 2])) fig.write_image("test.png")Thanks for the report. We're going to try to reproduce the issue locally and follow up.
I tried running both examples on Ubuntu Linux and didn't see any issue. What version of Chrome do you have installed?
I have the same issue. See the report below.
Confirming this issue on Kaleido 1.2.0 — significantly worse than 9 s per call
Environment:
OS: Windows 11 (10.0.26200)
Python: 3.12.8
Plotly: 6.5.2
Kaleido: 1.2.0 (previously: Kaleido 0.2.1 on a different machine, Plotly 6.0.0 — no issues)
Observed behaviour:Same "Resorting to unclean kill browser." message on every call. In my case each pio.to_image() takes approximately 14–15 seconds, which is worse than the 9 s reported above. Images are produced correctly, so this is purely a performance regression.
The severity becomes clear in any script that exports multiple figures. I have a report pipeline that calls pio.to_image() six times (three beam diagrams × two load cases). Profiling shows:
Step Duration
All structural beam analysis (SymPy + Plotly figure creation) 283 ms
PDF assembly — 6 × pio.to_image() via Kaleido 86,900 ms (~87 s)
The entire computation is effectively held hostage by Kaleido. The browser appears to be killed and restarted on every single call rather than staying warm between exports. On Kaleido 0.2.1 the same script completed in a few seconds total.Workaround found: None. Downgrading to Kaleido 0.2.1 is not straightforward because its API is incompatible with Plotly 6.x.
Update — same issue on Kaleido 1.3.0
Upgraded from 1.2.0 → 1.3.0 (choreographer 1.3.0). No change in behaviour.
Environment:
OS: Windows 11 (10.0.26200)
Python: 3.12.8
Plotly: 6.5.2
Kaleido: 1.3.0 (choreographer 1.3.0)
Detailed profiling of a 6-figure report:Duration Step
< 3 ms All non-Kaleido steps (cover, chapters, tables, ReportLab assembly)
10,031 ms fig_beam demoulding — pio.to_image() [KALEIDO]
9,426 ms fig_shear demoulding — pio.to_image() [KALEIDO]
10,133 ms fig_moment demoulding — pio.to_image() [KALEIDO]
9,543 ms fig_beam stacking — pio.to_image() [KALEIDO]
11,998 ms fig_shear stacking — pio.to_image() [KALEIDO]
9,870 ms fig_moment stacking — pio.to_image() [KALEIDO]
406 ms r.build() — ReportLab PDF assembly
~61 seconds TOTAL — 99.3% spent in Kaleido
Every single call takes ~10 seconds. The browser is clearly not being kept warm between exports. Non-Kaleido work (SymPy beam analysis, Plotly figure creation, ReportLab PDF layout) totals under 700 ms for the entire pipeline. The problem is entirely isolated to pio.to_image().What version of Chrome are you using?
I tried running both examples on Ubuntu Linux and didn't see any issue. What version of Chrome do you have installed?
Chrome version, installed on Windows:
Google Chrome | 146.0.7680.216 (Official Build) extended (64-bit) (cohort: 148.0.7778.56 Rollout) -- | -- Revision | ff9214069a282850ff7ebbef6615bb009cf45c57-refs/branch-heads/7680@{#4013} OS | Windows 11 Version 24H2 (Build 26100.8246) JavaScript | V8 14.6.202.34 User Agent | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 Command Line | "C:\Program Files\Google\Chrome\Application\chrome.exe" --flag-switches-begin --flag-switches-end Executable Path | C:\Program Files\Google\Chrome\Application\chrome.exeEven if I attempt to manually pin the google chrome location
import os os.environ["BROWSER_PATH"] = r"C:\Program Files\Google\Chrome\Application\chrome.exe" import plotly.graph_objects as go fig = go.Figure(go.Scatter(x=[1, 2, 3], y=[1, 3, 2])) fig.write_image("hello.png", width=200, height=200)
I will still get the same runtime error
Resorting to unclean kill browser. Traceback (most recent call last): File "C:\Users\username_redacted\Downloads\Code\dash-apps\test.py", line 7, in <module> fig.write_image("hello.png", width=200, height=200) File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\plotly\basedatatypes.py", line 3895, in write_image return pio.write_image(self, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\plotly\io\_kaleido.py", line 528, in write_image img_data = to_image( ^^^^^^^^^ File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\plotly\io\_kaleido.py", line 398, in to_image img_bytes = kaleido.calc_fig_sync( ^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\kaleido\__init__.py", line 170, in calc_fig_sync return _sync_server.oneshot_async_run(calc_fig, args=args, kwargs=kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\kaleido\_sync_server.py", line 161, in oneshot_async_run raise res File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\kaleido\_sync_server.py", line 152, in run q.put(asyncio.run(func(*args, **kwargs))) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 190, in run return runner.run(main) ^^^^^^^^^^^^^^^^ File "C:\Program Files\Python311\Lib\asyncio\runners.py", line 118, in run return self._loop.run_until_complete(task) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Program Files\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\kaleido\__init__.py", line 103, in calc_fig async with Kaleido(**kopts) as k: File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\kaleido\kaleido.py", line 297, in __aexit__ return await super().__aexit__(exc_type, exc_value, exc_tb) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\choreographer\browser_async.py", line 294, in __aexit__ await self.close() File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\kaleido\kaleido.py", line 273, in close await super().close() File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\choreographer\browser_async.py", line 270, in close await self._close() File "C:\Users\username_redacted\AppData\Roaming\Python\Python311\site-packages\choreographer\browser_async.py", line 256, in _close raise RuntimeError("Couldn't close or kill browser subprocess") RuntimeError: Couldn't close or kill browser subprocessHello, I'm the author of this thread.
In the end I managed to fix this by
Python 3.13.1
Kaleido 0.2.1
choreographer 1.2.1
nbformat 5.10.4
plotly 6.0.0It has been a while, I'm not sure which step worked but I haven't had any issue since.
I'm still working on investigating the slow time to shutdown Chrome, but here's an undocumented workaround for modern Kaleido: use
kaleido.start_sync_server()before callingwrite_image.Some background: calls to
write_imagestart up Chrome, render the image, then shut Chrome down. That can lead to long times to render if you're calling it multiple times.kaleido.start_sync_server()starts up Chrome and keeps it running until the script completes. So, multiple calls towrite_imageuse the same Chrome instance and you only have to wait for startup/shutdown once.Here's an example:
import kaleido import plotly.graph_objects as go kaleido.start_sync_server() # one-time; Chrome stays warm fig1 = go.Figure(go.Scatter(x=[1, 2, 3], y=[1, 3, 2])) fig1.write_image("a.png") # first call: cold start fig2 = go.Figure(go.Scatter(x=[1, 2, 3], y=[2, 1, 3])) fig2.write_image("b.png") # reuses warm Chrome
I'll get our README updated to mention this. @charity-g I'm going to add some better logging that might help us track down what's happening in your case. I'll ping you when that's available.
EDIT: README updated.
Reacted by b-a0
Using:
MacOS
uv for package management
Python 3.13.1
Kaleido 1.2.0
nbformat 5.10.4
choreographer 1.2.1
plotly 6.5.2
I have recently moved to a new computer (previously I was using Windows11, Plotly 6.0.0 and Kaleido 0.2.1) and now writing images is taking forever and throwing the "Resorting to unclean kill browser." message
For instance, this simple script:
is taking around 9 seconds.
The images are still being produced and saved correctly but the times are too long. Is there a fix?
Thank you