{"record":{"id":"ac1199ad5e831649","repo":"pandas-dev/pandas","slug":"intervals-must-all-be-closed-on-the-same-side","errorCode":null,"errorMessage":"Intervals must all be closed on the same side.","messagePattern":"Intervals must all be closed on the same side\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/interval.py","lineNumber":998,"sourceCode":"            and self.right.equals(other.right)\n        )\n\n    @classmethod\n    def _concat_same_type(cls, to_concat: Sequence[IntervalArray]) -> Self:\n        \"\"\"\n        Concatenate multiple IntervalArray\n\n        Parameters\n        ----------\n        to_concat : sequence of IntervalArray\n\n        Returns\n        -------\n        IntervalArray\n        \"\"\"\n        closed_set = {interval.closed for interval in to_concat}\n        if len(closed_set) != 1:\n            raise ValueError(\"Intervals must all be closed on the same side.\")\n        closed = closed_set.pop()\n\n        left: IntervalSide = np.concatenate([interval.left for interval in to_concat])\n        right: IntervalSide = np.concatenate([interval.right for interval in to_concat])\n\n        left, right, dtype = cls._ensure_simple_new_inputs(left, right, closed=closed)\n\n        return cls._simple_new(left, right, dtype=dtype)\n\n    def copy(self) -> Self:\n        \"\"\"\n        Return a copy of the array.\n\n        Returns\n        -------\n        IntervalArray\n        \"\"\"\n        left = self._left.copy()","sourceCodeStart":980,"sourceCodeEnd":1016,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/arrays/interval.py#L980-L1016","documentation":"Raised by IntervalArray._concat_same_type when concatenating two or more IntervalArrays whose 'closed' attribute differs (e.g., mixing 'left' with 'right'). Pandas requires all concatenated intervals to share one closure side because a single IntervalArray can only carry one 'closed' value. The check uses a set comprehension over each array's .closed and fails when more than one distinct value appears.","triggerScenarios":"Calling pd.concat on a list of Series/Index backed by IntervalArrays with different .closed, or IntervalArray._concat_same_type([...]) with mismatched closures, e.g. one array built with closed='left' and another closed='right'.","commonSituations":"Combining interval data sourced from different producers (cut/qcut defaults 'right' vs. user-built 'left'), merging interval columns created with explicit closed= arguments that disagree, or upgrading from versions where mixed concatenation was silently coerced.","solutions":["Normalize each IntervalArray's closure before concat by calling arr.set_closed('right') on every input so they share one closed value.","Rebuild the offending arrays from their breaks with a single closed= argument (e.g. pd.interval_range(..., closed='right')).","Filter or drop the arrays whose .closed differs rather than concatenating them."],"exampleFix":"# before\na = pd.arrays.IntervalArray.from_breaks([0,1,2], closed='left')\nb = pd.arrays.IntervalArray.from_breaks([2,3,4], closed='right')\npd.concat([pd.Series(a), pd.Series(b)])\n\n# after\na = a.set_closed('right')\nb = b.set_closed('right')\npd.concat([pd.Series(a), pd.Series(b)])","handlingStrategy":"validation","validationCode":"def safe_concat_interval(arrays):\n    closed_set = {a.closed for a in arrays}\n    if len(closed_set) != 1:\n        target = closed_set.pop()\n        arrays = [a.set_closed(target) for a in arrays]\n    return pd.concat([pd.Series(a) for a in arrays])","typeGuard":"def same_closed(arrays) -> bool:\n    return len({a.closed for a in arrays}) == 1","tryCatchPattern":null,"preventionTips":["Standardize 'closed' across all interval sources at ingest time.","Assert arr.closed == expected_closed before concat.","Document the closed convention for each interval column in your schema."],"tags":["interval-array","concat","validation"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}