{"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":"validation","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/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/interval.py#L980-L1016","documentation":"Raised by IntervalArray._concat_same_type (used by pd.concat / .append of IntervalArrays and IntervalIndexes) when the inputs do not all share the same 'closed' value. Intervals must be closed on a single consistent side to be concatenable.","triggerScenarios":"pd.concat([ia1, ia2]) where ia1.closed != ia2.closed; IntervalIndex.append([...]) with mismatched closed; concat of interval Series built with different closed conventions.","commonSituations":"Combining interval data from sources built with different defaults (from_breaks vs from_tuples); merging interval columns from separate ETL jobs.","solutions":["Normalize closed first: ia2 = ia2.set_closed(ia1.closed), then concat.","Align sources at construction time so they share closed.","Convert to a common non-interval representation (e.g., to_tuples) if mixing closed is intentional."],"exampleFix":"// before\npd.concat([ia1, ia2])  # closed mismatch\n// after\npd.concat([ia1, ia2.set_closed(ia1.closed)])","handlingStrategy":"validation","validationCode":"def concat_intervals(arrs):\n    closed = arrs[0].closed\n    if any(a.closed != closed for a in arrs):\n        arrs = [a.set_closed(closed) for a in arrs]\n    import pandas as pd\n    return pd.concat(arrs)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify .closed on every input before combining interval data.","Normalize closed via set_closed at ETL boundaries.","Build interval sources with a single consistent closed convention."],"tags":["interval-array","concat","closed"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}