pandas-dev/pandas · error · ValueError

invalid option for 'closed': {closed}

Error message

invalid option for 'closed': {closed}

What it means

Raised by IntervalArray.set_closed when the 'closed' argument is not one of {'left','right','both','neither'} (the VALID_CLOSED frozenset). Closure must be a known side specification; any other string/value is rejected before constructing the new dtype.

Source

Thrown at pandas/core/arrays/interval.py:1483

        --------
        IntervalArray.closed : Returns inclusive side of the Interval.
        arrays.IntervalArray.closed : Returns inclusive side of the IntervalArray.

        Examples
        --------
        >>> index = pd.arrays.IntervalArray.from_breaks(range(4))
        >>> index
        <IntervalArray>
        [(0, 1], (1, 2], (2, 3]]
        Length: 3, dtype: interval[int64, right]
        >>> index.set_closed("both")
        <IntervalArray>
        [[0, 1], [1, 2], [2, 3]]
        Length: 3, dtype: interval[int64, both]
        """
        if closed not in VALID_CLOSED:
            msg = f"invalid option for 'closed': {closed}"
            raise ValueError(msg)

        left, right = self._left, self._right
        dtype = IntervalDtype(left.dtype, closed=closed)
        return self._simple_new(left, right, dtype=dtype)

    @property
    def is_non_overlapping_monotonic(self) -> bool:
        """
        Return a boolean whether the IntervalArray/IntervalIndex\
        is non-overlapping and monotonic.

        Non-overlapping means (no Intervals share points), and monotonic means
        either monotonic increasing or monotonic decreasing.

        See Also
        --------
        overlaps : Check if two IntervalIndex objects overlap.

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Use one of the four literal values: 'left', 'right', 'both', or 'neither'.
  2. Lowercase and validate the input string before passing: closed if closed in {'left','right','both','neither'} else raise.
  3. Check your config source for stray quotes/whitespace and strip them.

Example fix

# before
arr = pd.arrays.IntervalArray.from_tuples([(0, 1)])
arr.set_closed('Right')

# after
arr.set_closed('right')
Defensive patterns

Strategy: validation

Validate before calling

VALID = {'left', 'right', 'both', 'neither'}
def normalize_closed(s):
    s = str(s).strip().lower()
    if s not in VALID:
        raise ValueError(f"closed must be one of {VALID}, got {s!r}")
    return s

Type guard

def is_valid_closed(s) -> bool:
    return isinstance(s, str) and s in {'left', 'right', 'both', 'neither'}

Prevention

When it happens

Trigger: Calling arr.set_closed('Right') (wrong case), arr.set_closed('closed'), arr.set_closed(None), or arr.set_closed('outside').

Common situations: Typos and capitalization mistakes ('Left' vs 'left'), passing a numeric flag, reading 'closed' from a config file that uses synonyms like 'open'/'closed'.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/1e0439088381c5ff. Report an issue: GitHub.