pandas-dev/pandas · error · ValueError

missing values must be missing in the same location both lef

Error message

missing values must be missing in the same location both left and right sides

What it means

Raised by `_validate` when the NA mask of `left` differs from the NA mask of `right`. An interval is either fully present or fully missing at each position; a half-NaN interval is meaningless. Fires at pandas/core/arrays/interval.py:620.

Source

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

        * dtype is correct
        * left and right match lengths
        * left and right have the same missing values
        * left is always below right
        """
        if not isinstance(dtype, IntervalDtype):
            msg = f"invalid dtype: {dtype}"
            raise ValueError(msg)
        if len(left) != len(right):
            msg = "left and right must have the same length"
            raise ValueError(msg)
        left_mask = notna(left)
        right_mask = notna(right)
        if not (left_mask == right_mask).all():
            msg = (
                "missing values must be missing in the same "
                "location both left and right sides"
            )
            raise ValueError(msg)
        if not (left[left_mask] <= right[left_mask]).all():
            msg = "left side of interval must be <= right side"
            raise ValueError(msg)

    def _shallow_copy(self, left, right) -> Self:
        """
        Return a new IntervalArray with the replacement attributes

        Parameters
        ----------
        left : Index
            Values to be used for the left-side of the intervals.
        right : Index
            Values to be used for the right-side of the intervals.
        """
        dtype = IntervalDtype(left.dtype, closed=self.closed)
        left, right, dtype = self._ensure_simple_new_inputs(left, right, dtype=dtype)

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Co-locate NA: `mask = left.isna() | right.isna(); left[mask] = right[mask] = np.nan`.
  2. Drop rows where either side is NA before constructing: `df.dropna(subset=['lo','hi'])`.
  3. Impute the missing bound from domain knowledge before building intervals.

Example fix

// before
pd.IntervalIndex.from_arrays(df['lo'], df['hi'])  # NA misaligned
// after
m = df['lo'].isna() | df['hi'].isna()
df = df.loc[~m]
pd.IntervalIndex.from_arrays(df['lo'], df['hi'])
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np
import pandas as pd

def colocate_na(left, right):
    left = pd.Series(left)
    right = pd.Series(right)
    mask = left.isna() | right.isna()
    left[mask] = np.nan
    right[mask] = np.nan
    return left.to_numpy(), right.to_numpy()

Type guard

import pandas as pd

def na_masks_match(left, right) -> bool:
    return (pd.notna(left) == pd.notna(right)).all()

Try / catch

try:
    ia = pd.IntervalArray(left, right)
except ValueError as e:
    if "missing in the same location" in str(e):
        l, r = colocate_na(left, right)
        ia = pd.IntervalArray(l, r)
    else:
        raise

Prevention

When it happens

Trigger: `from_arrays([1, np.nan, 3], [2, 4, np.nan])` — position 1 has NaN left but a real right value.

Common situations: Joining bound columns where one has missing timestamps and the other does not; ETL that nulls only one endpoint.

Related errors


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