{"record":{"id":"27a1fd4568d95f08","repo":"pandas-dev/pandas","slug":"periods-must-be-an-integer","errorCode":null,"errorMessage":"periods must be an integer","messagePattern":"periods must be an integer","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/algorithms.py","lineNumber":1521,"sourceCode":"    ----------\n    arr : ndarray or ExtensionArray\n    n : int\n        number of periods\n    axis : {0, 1}\n        axis to shift on\n    stacklevel : int, default 3\n        The stacklevel for the lost dtype warning.\n\n    Returns\n    -------\n    shifted\n    \"\"\"\n\n    # added a check on the integer value of period\n    # see https://github.com/pandas-dev/pandas/issues/56607\n    if not lib.is_integer(n):\n        if not (is_float(n) and n.is_integer()):\n            raise ValueError(\"periods must be an integer\")\n        n = int(n)\n    na = np.nan\n    dtype = arr.dtype\n\n    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__}__\"):","sourceCodeStart":1503,"sourceCodeEnd":1539,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/algorithms.py#L1503-L1539","documentation":"Raised by pandas.core.algorithms.diff when the periods argument n is not an integer. diff shifts by n positions then subtracts; a non-integral shift count is meaningless, so only integer-valued types (or floats that represent whole numbers like 2.0) are accepted.","triggerScenarios":"s.diff(1.5), df.diff(periods='1'), or passing a float NaN/None-derived n that is neither an integer nor a whole-number float; calling diff with a config value parsed as a float.","commonSituations":"Period values read from JSON/config as strings or floats; computations that produce fractional period counts; np.float64 values that are not whole numbers.","solutions":["Pass an int: s.diff(1).","If the value comes in as a float, coerce only when whole: int(n) after verifying n.is_integer().","Validate the input type at your boundary before diff."],"exampleFix":"# before\ns.diff(1.5)\n# after\ns.diff(int(1))","handlingStrategy":"validation","validationCode":"import numpy as np\n\ndef safe_diff(s, n):\n    if isinstance(n, float):\n        if not n.is_integer():\n            raise ValueError('periods must be an integer')\n        n = int(n)\n    elif not isinstance(n, (int, np.integer)):\n        raise ValueError('periods must be an integer')\n    return s.diff(n)","typeGuard":"import numpy as np\n\ndef is_integer_periods(n) -> bool:\n    if isinstance(n, (int, np.integer)):\n        return True\n    return isinstance(n, float) and n.is_integer()","tryCatchPattern":null,"preventionTips":["Pass ints to diff periods; coerce whole-number floats to int first.","Parse config values as int before diff.","Validate the type at your boundary."],"tags":["diff","periods","integer","validation"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}