matplotlib/matplotlib · error · TypeError

np.datetime64 'position' values require np.timedelta64 'widt

Error message

np.datetime64 'position' values require np.timedelta64 'widths'

What it means

TypeError raised by the shared violinplot validation when positions[0] is a numpy datetime64 but widths[0] is not a numpy timedelta64. This is the numpy twin of the datetime/date check, with one extra trap: a plain Python datetime.timedelta is NOT an instance of np.timedelta64, so date positions converted to numpy with .to_numpy() plus Python-timedelta widths still fails.

Source

Thrown at lib/matplotlib/axes/_axes.py:9293

            widths = [widths] * N
        elif len(widths) != N:
            raise ValueError(datashape_message.format("widths"))

        # For usability / better error message:
        # Validate that datetime-like positions have timedelta-like widths.
        # Checking only the first element is good enough for standard misuse cases
        if N > 0:  # No need to validate if there is no data
            pos0 = positions[0]
            width0 = widths[0]
            if (isinstance(pos0, (datetime.datetime, datetime.date))
                and not isinstance(width0, datetime.timedelta)):
                raise TypeError(
                    "datetime/date 'position' values require timedelta 'widths'. "
                    "For example, use positions=[datetime.date(2024, 1, 1)] "
                    "and widths=[datetime.timedelta(days=1)].")
            elif (isinstance(pos0, np.datetime64)
                and not isinstance(width0, np.timedelta64)):
                raise TypeError(
                    "np.datetime64 'position' values require np.timedelta64 'widths'")
        _api.check_in_list(["both", "low", "high"], side=side)

        # Calculate ranges for statistics lines (shape (2, N)).
        line_ends = [[-0.25 if side in ['both', 'low'] else 0],
                     [0.25 if side in ['both', 'high'] else 0]] \
                          * np.array(widths) + positions

        # Make a cycle of color to iterate through, using 'none' as fallback
        def cycle_color(color, alpha=None):
            rgba = mcolors.to_rgba_array(color, alpha=alpha)
            color_cycler = itertools.chain(itertools.cycle(rgba),
                                           itertools.repeat('none'))
            color_list = []
            for _ in range(N):
                color_list.append(next(color_cycler))
            return color_list

View on GitHub (pinned to b379c1b69e)

Solutions

  1. Use numpy timedelta widths: widths=np.timedelta64(1, 'D') (scalar broadcasts).
  2. Convert existing Python timedeltas: np.timedelta64(pd.Timedelta(days=1)) or np.timedelta64(datetime.timedelta(days=1)).
  3. Alternatively convert positions to floats via matplotlib.dates.date2num and keep numeric widths.

Example fix

# before
ax.violinplot(data, positions=daily_index.to_numpy(), widths=0.5)

# after
ax.violinplot(data, positions=daily_index.to_numpy(),
              widths=np.timedelta64(1, 'D'))
Defensive patterns

Strategy: type-guard

Validate before calling

import numpy as np

def coerce_violin_widths_np(positions, widths):
    """np.datetime64 positions need np.timedelta64 widths (not datetime.timedelta)."""
    n = len(positions)
    widths = [widths] * n if np.isscalar(widths) else list(widths)
    if isinstance(positions[0], np.datetime64):
        widths = [w if isinstance(w, np.timedelta64)
                  else np.timedelta64(w, 'D') for w in widths]
    return widths

Type guard

import numpy as np

def violin_np_datetime_ok(positions, widths) -> bool:
    if len(positions) == 0:
        return True
    w0 = widths if np.isscalar(widths) else widths[0]
    if isinstance(positions[0], np.datetime64):
        return isinstance(w0, np.timedelta64)  # datetime.timedelta does NOT count
    return True

Try / catch

try:
    ax.violinplot(data, positions=positions, widths=widths)
except TypeError as e:
    if 'np.datetime64' in str(e):
        ax.violinplot(data, positions=positions,
                      widths=np.timedelta64(1, 'D'))
    else:
        raise

Prevention

When it happens

Trigger: ax.violinplot(data, positions=daily_index.to_numpy(), widths=0.5); positions of dtype datetime64 combined with widths=[datetime.timedelta(days=1)] (Python timedelta instead of np.timedelta64).

Common situations: Pandas DatetimeIndex values converted to numpy datetime64; time-indexed box/violin charts after a pandas-to-numpy conversion where widths were written for the Python datetime branch.

Related errors


AI-assisted analysis of matplotlib/matplotlib@b379c1b69e (2026-08-21). Data as JSON: /api/errors/7ffd41d5a0f3d43d. Report an issue: GitHub.