Skip to content

ValueError: Cannot mask with non-boolean array containing NA / NaN values

Has a filter failed because the condition itself has gaps in it?

sales = pd.DataFrame({'region': ['North', 'South', 'North'],
                      'revenue': [100, 250, 175]})

sales[pd.Series([True, np.nan, False])]
ValueError: Cannot mask with non-boolean array containing NA / NaN values

A mask answers one question per row: keep it or drop it. NaN is neither, and Pandas will not decide on your behalf whether unknown means yes or no.

Say which you meant, and it works:

sales[pd.Series([True, np.nan, False]).fillna(False).astype(bool)]
  region  revenue
0  North      100

Where the NaN in the mask comes from

Not from typing one. It comes from comparing a column that has missing values, because a comparison against NaN produces NaN rather than False:

sales['revenue'] > 150        # fine, no missing data
raw['revenue'] > 150          # NaN wherever revenue is missing

So the error is really telling you something about the data rather than about the filter: the column you filtered on has gaps you had not accounted for.

That is worth a moment before reaching for fillna(False). Rows with missing revenue are not rows with low revenue, and silently excluding them may or may not be the analysis you want. Sometimes the right move is to find out why they are missing first.

The two honest answers

Treat missing as "does not qualify":

raw[(raw['revenue'] > 150).fillna(False)]

Or exclude the rows with missing data explicitly, which documents the decision in the code:

raw[raw['revenue'].notna() & (raw['revenue'] > 150)]

I prefer the second when the missing values are meaningful, because a reader can see that a choice was made. The first is fine when missing genuinely means "no".

Why plain NumPy floats behave differently

If the same comparison on a NumPy-backed float column gives you False instead of NaN, that is not a contradiction — comparisons involving NaN return False under IEEE rules for plain floats. The NaN-propagating behavior comes from Pandas' nullable dtypes, where missing is tracked separately and a comparison against unknown is unknown.

Which you have depends on how the column was created and read. It is the clearest practical difference between the two families of dtype, and this error is often the first place someone meets it.

The mistakes I see most often

Using fillna(False) without astype(bool). Filling a float column with False gives you 0.0, which is still not a boolean, and the next error is about the dtype.

Filtering before cleaning. Handling missing values when the data is loaded removes a whole family of downstream surprises.

Confusing this with the unalignable-mask error. If the message mentions the index rather than NA values, the mask's labels are the problem, not its contents.

Practice

See notna in Pandas for finding complete rows, fillna in Pandas for filling gaps, and loc in Pandas for selecting by condition.