ALL software version info
Python : 3.13.13 (main, May 10 2026, 19:26:54) [Clang 22.1.3 ]
Operating system : Linux-6.17.0-22-generic-x86_64-with-glibc2.42
holoviews : 1.22.1
bokeh : 3.9.0
numpy : 2.4.4
panel : 1.9.0
param : 2.4.0
Description of expected behavior and the observed behavior
Rendering a Curve with zero data points and interpolation="steps-post" (or "steps-pre") crashes with ValueError: negative dimensions are not allowed.
The root cause is in holoviews/operation/element.py. interpolate_curve.pts_to_poststep computes:
steps = np.zeros(2 * len(x) - 1)
For an empty input array (len(x) == 0), this becomes np.zeros(-1), which numpy rejects. The same 2 * len(x) - 1 pattern appears in pts_to_prestep. pts_to_midstep uses 2 * len(x) which is safer but the value-array dtype handling on empty input is still untested.
Expected: an empty Curve should render as an empty curve (no error).
Observed: ValueError: negative dimensions are not allowed.
This crashes any interactive workflow where a stream-driven operation (e.g. downsample1d masked to an x-window that contains zero rows) feeds an empty Curve into the step-interpolation path. In our use case (downsample1d + RangeX on tick-level financial data, with users zooming into sub-second windows), the empty Curve appears intermittently. The crash then propagates up through Stream.trigger's subscriber loop (holoviews/streams.py:213), which has no try/except, silently cancelling every subsequent subscriber's refresh — so any sibling plots in the same trigger pass stay frozen on stale data, even though their own downsample1d would have produced valid output. This makes the bug particularly nasty to diagnose: the visible symptom is a chart stuck on a zoomed-in sample with no error visible in the browser, while the actual exception fires once in the Python kernel for an unrelated empty-curve operation.
Complete, minimal, self-contained example code that reproduces the issue
import holoviews as hv
hv.extension("bokeh")
# An empty Curve with steps-post interpolation.
empty_curve = hv.Curve(([], []), "x", "y").opts(interpolation="steps-post")
# Force render → triggers interpolate_curve via the bokeh backend.
hv.render(empty_curve)
interpolation="steps-pre" reproduces identically. interpolation="linear" (the default) does not crash since it doesn't go through interpolate_curve.
Stack traceback and/or browser JavaScript console output
Traceback (most recent call last):
...
File "holoviews/plotting/bokeh/chart.py", line 466, in get_data
element = interpolate_curve(element, interpolation=self.interpolation)
File "param/parameterized.py", line 6335, in __new__
return inst.__call__(*args, **params)
File "holoviews/core/operation.py", line 218, in __call__
return element.apply(self, **kwargs)
File "holoviews/core/accessors.py", line 200, in __call__
new_obj = apply_function(self._obj, **inner_kwargs)
File "holoviews/core/operation.py", line 212, in __call__
return self._apply(element)
File "holoviews/core/operation.py", line 141, in _apply
ret = self._process(element, key)
File "holoviews/operation/element.py", line 1142, in _process
return element.map(self._process_layer, Element)
File "holoviews/core/dimension.py", line 761, in map
return map_fn(self) if applies else self
File "holoviews/operation/element.py", line 1136, in _process_layer
xs, dvals = INTERPOLATE_FUNCS[self.p.interpolation](x, dvals)
File "holoviews/operation/element.py", line 1110, in pts_to_poststep
steps = np.zeros(2 * len(x) - 1)
ValueError: negative dimensions are not allowed
Suggested fix
Early-return on empty input in pts_to_poststep, pts_to_prestep, and pts_to_midstep. Roughly:
@classmethod
def pts_to_poststep(cls, x, values):
if len(x) == 0:
empty_x = np.empty(0, dtype=x.dtype if hasattr(x, "dtype") else float)
empty_vals = tuple(np.empty(0, dtype=cls._get_dtype(v)) for v in values)
return empty_x, empty_vals
# ... existing implementation ...
A related secondary concern — that Stream.trigger's subscriber loop has no per-subscriber error isolation, so this single crash silently freezes every later subscriber's plot — is filed separately as .
ALL software version info
Description of expected behavior and the observed behavior
Rendering a
Curvewith zero data points andinterpolation="steps-post"(or"steps-pre") crashes withValueError: negative dimensions are not allowed.The root cause is in
holoviews/operation/element.py.interpolate_curve.pts_to_poststepcomputes:For an empty input array (
len(x) == 0), this becomesnp.zeros(-1), which numpy rejects. The same2 * len(x) - 1pattern appears inpts_to_prestep.pts_to_midstepuses2 * len(x)which is safer but the value-array dtype handling on empty input is still untested.Expected: an empty Curve should render as an empty curve (no error).
Observed:
ValueError: negative dimensions are not allowed.This crashes any interactive workflow where a stream-driven operation (e.g.
downsample1dmasked to an x-window that contains zero rows) feeds an empty Curve into the step-interpolation path. In our use case (downsample1d+RangeXon tick-level financial data, with users zooming into sub-second windows), the empty Curve appears intermittently. The crash then propagates up throughStream.trigger's subscriber loop (holoviews/streams.py:213), which has no try/except, silently cancelling every subsequent subscriber's refresh — so any sibling plots in the same trigger pass stay frozen on stale data, even though their owndownsample1dwould have produced valid output. This makes the bug particularly nasty to diagnose: the visible symptom is a chart stuck on a zoomed-in sample with no error visible in the browser, while the actual exception fires once in the Python kernel for an unrelated empty-curve operation.Complete, minimal, self-contained example code that reproduces the issue
interpolation="steps-pre"reproduces identically.interpolation="linear"(the default) does not crash since it doesn't go throughinterpolate_curve.Stack traceback and/or browser JavaScript console output
Suggested fix
Early-return on empty input in
pts_to_poststep,pts_to_prestep, andpts_to_midstep. Roughly:A related secondary concern — that
Stream.trigger's subscriber loop has no per-subscriber error isolation, so this single crash silently freezes every later subscriber's plot — is filed separately as .