JS errors with xyz.OpenStreetMap.Mapnik

While following this example (tile_xyzservices — Bokeh 3.6.2 Documentation) I have noticed some some uncaught error that are related to tiles based on
xyz.OpenStreetMap.Mapnik. This also leads to layout issues (tabs overlay).
Any idea what is going on?

Uncaught Error: Assertion failed
    a http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:178
    c http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:178
    get_extent http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:659
    _set_data http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:659
    _paint http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:659
    paint http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:249
    _paint_levels http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:623
    _actual_paint http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:623
    paint http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:623
    _after_layout http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:623
    after_layout http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:588
    after_layout http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:588
    after_layout http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:588
    after_layout http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:588
    compute_layout http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:588
    invalidate_layout http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:588
    _after_render http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:588
    after_render http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:277
    r_after_render http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:223
    _after_resize http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:588
    after_resize http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:277
    _resize_observer http://localhost:5006/static/js/bokeh.min.js?v=7b53bd5c9e3da7dab0a6954653d5ff549d082f58d03eafe7e0b956b690138e3788c99fc68ad65e005b304417171f0369d8f3a2fc678ad79d9a58890a7e656baf:277
from bokeh.io import curdoc
from bokeh.models import TabPanel, Tabs
from bokeh.plotting import figure

import xyzservices.providers as xyz

t1 = figure(x_axis_type="mercator", y_axis_type="mercator", sizing_mode="stretch_both", match_aspect=True)
t1.add_tile(xyz.OpenStreetMap.Mapnik) # Note: This will trigger a JS assertion error

app_layout = Tabs(tabs=[
    TabPanel(child=t1, title="Rides"),
    TabPanel(child=figure(), title="Analytics"),
],
    sizing_mode="stretch_both"
)

curdoc().add_root(app_layout)

@Maxxner your plot has default data-ranges but you have not plotted any data. I am pretty sure you either need to:

  • add some glyphs so that DataRange1d has something to “work with”, or
  • manually set the plot ranges explicitly yourself

Note that on the Python side in the server log there is this warning:

2025-02-03 12:11:08,870 W-1000 (MISSING_RENDERERS): Plot has no renderers: figure(id='p1039', ...)

which is specifically intended to draw attention to the fact that a plot with no data renderers is usually not “correct” or what is intended.

@Bryan I get your point however I’d like to emphasize that:

  • Those hard errors only appear with tiles. I’m totally okay with a white background showing, as it is typcially done in case of no renderers (e.g. figure without anything).
  • A side effect of the error is layout corruption which means other areas outside the plot are also effected. This is actually more of my concern as I would expect missing data to only affect a plot itself and not the whole layout.

Coming back to your suggestions. In my case the data & therefore the glyph is added via file input which means I could only set some dummy values (which I would like to avoid). Additionally the figure is intended to have autoscaling based on the file data that will be added. If I set a manual plot range at the beginning, autoscaling is disable. Afaik there is no option to active autoscaling later after explicitely having set the range before (please correct me if I’m wrong here).

I don’t mean to sound frustrated but I am really not sure what users (many, not just you) expect here over the years. Automatic data-ranging requires some data as input to start, definitionally.

I agree that if hard errors only occur for tiles then that specifically could be an actionable bug to report.

@Bryan I’m not sure if you fully understood what I am trying to express.

The quote is referring to this path. Basically the question is how one trigger autoscaling later once the data arrives if an explicite range has been set before when no data was available.
no data → explicite range → add data → autoscale range

There is not currently a path for that, which is what I have been trying to convey.

All of these considerations crash together in data ranges: log vs linear scale, constraints for aspect preservation, min and max intervals, and min/max/auto bounds, multiple axes, automatic “data following” mode, responding to events for every interactive tool for panning, zooming, scrolling, range tools, and more. It’s an area of uncomfortably many irreducible and difficult-to-test entanglements, it is one of the single most gnarly places in the whole BokehJS codebase, by far.

So, while I suppose it is possible in principle to add something like “fallback start and end” to use in case data-ranging fails, I would be extremely reluctant to add any more complexity to data ranges at this point.

By far the usual use case is to start off a plot with something plotted. For the relatively rare case (IME) you describe, I’d suggest adding some invisible glyph to give the data ranges something to chew on. A kludge? I guess, but a simple one. If you think it’s worth adding a note about this one case in the data-range docstring that’s certainly a PR I’d accept.

@Bryan Gotcha. Nevertheless I’m still wondering if the complexity issues still arises even if one just wants to reapply the autoranging (basically data is there, everything is in place, just range had explicite start/end values before)? After all data changes should trigger a similar data-ranging pipeline (of course only with active auto-ranging).

For the relatively rare case (IME) you describe, I’d suggest adding some invisible glyph to give the data ranges something to chew on. A kludge? I guess, but a simple one.

Out of curiotisity, is adding data later based on user input really such a rare use case? Maybe I’m the exception here but to me this feels like a common secnario, at least in a server setting.