{"record":{"id":"7ffd41d5a0f3d43d","repo":"matplotlib/matplotlib","slug":"np-datetime64-position-values-require-np-timedel","errorCode":null,"errorMessage":"np.datetime64 'position' values require np.timedelta64 'widths'","messagePattern":"np\\.datetime64 'position' values require np\\.timedelta64 'widths'","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/matplotlib/axes/_axes.py","lineNumber":9293,"sourceCode":"            widths = [widths] * N\n        elif len(widths) != N:\n            raise ValueError(datashape_message.format(\"widths\"))\n\n        # For usability / better error message:\n        # Validate that datetime-like positions have timedelta-like widths.\n        # Checking only the first element is good enough for standard misuse cases\n        if N > 0:  # No need to validate if there is no data\n            pos0 = positions[0]\n            width0 = widths[0]\n            if (isinstance(pos0, (datetime.datetime, datetime.date))\n                and not isinstance(width0, datetime.timedelta)):\n                raise TypeError(\n                    \"datetime/date 'position' values require timedelta 'widths'. \"\n                    \"For example, use positions=[datetime.date(2024, 1, 1)] \"\n                    \"and widths=[datetime.timedelta(days=1)].\")\n            elif (isinstance(pos0, np.datetime64)\n                and not isinstance(width0, np.timedelta64)):\n                raise TypeError(\n                    \"np.datetime64 'position' values require np.timedelta64 'widths'\")\n        _api.check_in_list([\"both\", \"low\", \"high\"], side=side)\n\n        # Calculate ranges for statistics lines (shape (2, N)).\n        line_ends = [[-0.25 if side in ['both', 'low'] else 0],\n                     [0.25 if side in ['both', 'high'] else 0]] \\\n                          * np.array(widths) + positions\n\n        # Make a cycle of color to iterate through, using 'none' as fallback\n        def cycle_color(color, alpha=None):\n            rgba = mcolors.to_rgba_array(color, alpha=alpha)\n            color_cycler = itertools.chain(itertools.cycle(rgba),\n                                           itertools.repeat('none'))\n            color_list = []\n            for _ in range(N):\n                color_list.append(next(color_cycler))\n            return color_list\n","sourceCodeStart":9275,"sourceCodeEnd":9311,"githubUrl":"https://github.com/matplotlib/matplotlib/blob/b379c1b69e012b142c0f496a52bcb30513802d72/lib/matplotlib/axes/_axes.py#L9275-L9311","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Use numpy timedelta widths: widths=np.timedelta64(1, 'D') (scalar broadcasts).","Convert existing Python timedeltas: np.timedelta64(pd.Timedelta(days=1)) or np.timedelta64(datetime.timedelta(days=1)).","Alternatively convert positions to floats via matplotlib.dates.date2num and keep numeric widths."],"exampleFix":"# before\nax.violinplot(data, positions=daily_index.to_numpy(), widths=0.5)\n\n# after\nax.violinplot(data, positions=daily_index.to_numpy(),\n              widths=np.timedelta64(1, 'D'))","handlingStrategy":"type-guard","validationCode":"import numpy as np\n\ndef coerce_violin_widths_np(positions, widths):\n    \"\"\"np.datetime64 positions need np.timedelta64 widths (not datetime.timedelta).\"\"\"\n    n = len(positions)\n    widths = [widths] * n if np.isscalar(widths) else list(widths)\n    if isinstance(positions[0], np.datetime64):\n        widths = [w if isinstance(w, np.timedelta64)\n                  else np.timedelta64(w, 'D') for w in widths]\n    return widths","typeGuard":"import numpy as np\n\ndef violin_np_datetime_ok(positions, widths) -> bool:\n    if len(positions) == 0:\n        return True\n    w0 = widths if np.isscalar(widths) else widths[0]\n    if isinstance(positions[0], np.datetime64):\n        return isinstance(w0, np.timedelta64)  # datetime.timedelta does NOT count\n    return True","tryCatchPattern":"try:\n    ax.violinplot(data, positions=positions, widths=widths)\nexcept TypeError as e:\n    if 'np.datetime64' in str(e):\n        ax.violinplot(data, positions=positions,\n                      widths=np.timedelta64(1, 'D'))\n    else:\n        raise","preventionTips":["Remember a Python datetime.timedelta is not a np.timedelta64 for this check.","Convert explicitly: np.timedelta64(pd.Timedelta(days=1)).","Use matplotlib.dates.date2num positions to keep everything numeric."],"tags":["matplotlib","violinplot","numpy-datetime","timedelta64","typeerror"],"backgroundTag":"datetime-type-mismatch","analyzedSha":"b379c1b69e012b142c0f496a52bcb30513802d72","analyzedAt":"2026-08-21T23:31:55.468Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}