{"record":{"id":"c5a67146cf557c9b","repo":"pandas-dev/pandas","slug":"type-arr-name-has-no-diff-method-convert","errorCode":null,"errorMessage":"{type(arr).__name__} has no 'diff' method. Convert to a suitable dtype prior to calling 'diff'.","messagePattern":"(.+?) has no 'diff' method\\. Convert to a suitable dtype prior to calling 'diff'\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pandas/core/algorithms.py","lineNumber":1544,"sourceCode":"    is_bool = is_bool_dtype(dtype)\n    if is_bool:\n        op = operator.xor\n    else:\n        op = operator.sub\n\n    if isinstance(dtype, NumpyEADtype):\n        # NumpyExtensionArray cannot necessarily hold shifted versions of itself.\n        arr = arr.to_numpy()\n        dtype = arr.dtype\n\n    if not isinstance(arr, np.ndarray):\n        # i.e ExtensionArray\n        if hasattr(arr, f\"__{op.__name__}__\"):\n            if axis >= arr.ndim:\n                raise ValueError(f\"cannot diff {type(arr).__name__} on axis={axis}\")\n            return op(arr, arr.shift(n))\n        else:\n            raise TypeError(\n                f\"{type(arr).__name__} has no 'diff' method. \"\n                \"Convert to a suitable dtype prior to calling 'diff'.\"\n            )\n\n    is_timedelta = False\n    if arr.dtype.kind in \"mM\":\n        dtype = np.int64\n        arr = arr.view(\"i8\")\n        na = iNaT\n        is_timedelta = True\n\n    elif is_bool:\n        # We have to cast in order to be able to hold np.nan\n        dtype = np.object_\n\n    elif dtype.kind in \"iu\":\n        # We have to cast in order to be able to hold np.nan\n","sourceCodeStart":1526,"sourceCodeEnd":1562,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/algorithms.py#L1526-L1562","documentation":"Raised by pandas.core.algorithms.diff when the input ExtensionArray does not define a subtraction operator (__sub__/__rsub__) at all, so differencing is impossible for its dtype. The user is told to convert to a suitable dtype before calling diff.","triggerScenarios":"s.diff() on a Series backed by an ExtensionArray whose dtype has no subtraction semantics (e.g. some string/object ExtensionArrays, boolean ExtensionArray without numeric cast); calling diff on custom ExtensionArray types that omit arithmetic.","commonSituations":"Applying diff generically across mixed-dtype DataFrames where some columns are non-numeric ExtensionArrays; upgrading to pyarrow/string dtypes then calling diff on those columns.","solutions":["Convert to a numeric dtype before diff: pd.to_numeric(s).diff().","Select only numeric columns when diffing a DataFrame (select_dtypes(include='number')).","Implement __sub__ on a custom ExtensionArray if differencing should be supported."],"exampleFix":"# before\ns = pd.Series(['1', '2', '3'], dtype='string')\ns.diff()\n# after\npd.to_numeric(s).diff()","handlingStrategy":"validation","validationCode":"import pandas as pd\n\ndef diff_numeric(s, n=1, axis=0):\n    if not pd.api.types.is_numeric_dtype(s):\n        s = pd.to_numeric(s, errors='coerce')\n    return s.diff(n, axis=axis)","typeGuard":"import pandas as pd\n\ndef is_diffable(s) -> bool:\n    return pd.api.types.is_numeric_dtype(s) or pd.api.types.is_timedelta64_dtype(s)","tryCatchPattern":"try:\n    return s.diff()\nexcept TypeError:\n    return pd.to_numeric(s, errors='coerce').diff()","preventionTips":["Convert non-numeric ExtensionArrays with pd.to_numeric before diff.","Restrict diff to numeric columns (select_dtypes(include='number')).","Implement __sub__ on custom ExtensionArrays that need differencing."],"tags":["diff","extension-array","dtype","conversion"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}