Describe the issue:
A segmentation fault occurs when performing a compound in-place boolean OR operation on a temporary comparison result with arrays of 262,144 (2^18) or more elements on Python 3.14.2 on Linux (standard GIL-enabled build, not free-threaded).
The expression condition |= array == scalar, where condition is a Python bool (False) and array is a float32 ndarray, causes a segfault when the array has 512x512 or more elements. Arrays of 511x511 or smaller work without issue.
Critically, separating the expression into two steps avoids the crash entirely:
result = array == scalar # step 1: evaluate comparison
condition |= result # step 2: in-place OR with named result
This suggests the issue is related to the lifetime management of the temporary array produced by array == scalar when it is immediately consumed by the augmented assignment |=. On Python 3.14, CPython's internal reference counting has been reworked (biased reference counting for PEP 703 support, even in the standard GIL-enabled build), which may cause the temporary to be released or its memory to be reused before the |= operation has finished reading from it.
The threshold at exactly 2^18 elements (512x512) points to an internal numpy buffer or dispatch boundary that changes how the operation is executed. Below this threshold, a different code path is taken that does not exhibit the bug.
This bug was originally discovered through xarray, whose _apply_mask function uses this exact pattern to replace _FillValue entries with NaN during CF decoding of NetCDF files. The upstream xarray issue is here: pydata/xarray#11205
Reproduce the code example:
import numpy as np
# --- Minimal reproducer ---
# Segfaults on Python 3.14.2 Linux (standard GIL build):
d = np.random.rand(512, 512).astype(np.float32)
condition = False
condition |= d == np.float32(65534.0)
# --- Control cases that all work fine ---
# 1. Same operation on 511x511 (below threshold): OK
d_small = np.random.rand(511, 511).astype(np.float32)
condition_small = False
condition_small |= d_small == np.float32(65534.0)
print("511x511: OK")
# 2. Comparison alone on 512x512: OK
d = np.random.rand(512, 512).astype(np.float32)
result = d == np.float32(65534.0)
print(f"512x512 comparison alone: OK, shape={result.shape}")
# 3. In-place OR with a pre-computed result on 512x512: OK
condition = False
condition |= result
print("512x512 |= with pre-computed result: OK")
# 4. Non-augmented OR in single expression on 512x512: OK
condition = False | (d == np.float32(65534.0))
print("512x512 non-augmented OR: OK")
Error message:
Fatal Python error: Segmentation fault
Current thread 0x00007afea9c58740 [python] (most recent call first):
File "/mnt/d/poly/dask_tests/.venv314/lib/python3.14/site-packages/xarray/coding/variables.py", line 132 in _apply_mask
File "/mnt/d/poly/dask_tests/.venv314/lib/python3.14/site-packages/xarray/coding/common.py", line 80 in get_duck_array
File "/mnt/d/poly/dask_tests/.venv314/lib/python3.14/site-packages/xarray/core/indexing.py", line 924 in get_duck_array
File "/mnt/d/poly/dask_tests/.venv314/lib/python3.14/site-packages/xarray/core/indexing.py", line 970 in get_duck_array
File "/mnt/d/poly/dask_tests/.venv314/lib/python3.14/site-packages/xarray/core/indexing.py", line 604 in __array__
File "/mnt/d/poly/dask_tests/.venv314/lib/python3.14/site-packages/xarray/core/variable.py", line 336 in _as_array_or_item
File "/mnt/d/poly/dask_tests/.venv314/lib/python3.14/site-packages/xarray/core/variable.py", line 556 in values
File "/mnt/d/poly/dask_tests/.venv314/lib/python3.14/site-packages/xarray/core/dataarray.py", line 798 in values
File "/mnt/d/poly/dask_tests/test_crash.py", line 23 in main
File "/mnt/d/poly/dask_tests/test_crash.py", line 27 in <module>
Current thread's C stack trace (most recent call first):
[1] 202839 segmentation fault (core dumped) python -X faulthandler test_crash.py
Python and NumPy Versions:
Python: 3.14.2 [Clang 21.1.4]
numpy==2.4.2
Runtime Environment:
[{'numpy_version': '2.4.2',
'python': '3.14.2 (main, Jan 27 2026, 23:59:57) [Clang 21.1.4 ]',
'uname': uname_result(system='Linux', node='', release='6.6.87.2-microsoft-standard-WSL2', version='#1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025', machine='x86_64')},
{'simd_extensions': {'baseline': ['X86_V2'],
'found': ['X86_V3'],
'not_found': ['X86_V4', 'AVX512_ICL', 'AVX512_SPR']}},
{'ignore_floating_point_errors_in_matmul': False},
{'architecture': 'Haswell',
'filepath': '/mnt/d/poly/dask_tests/.venv314/lib/python3.14/site-packages/numpy.libs/libscipy_openblas64_-096271d3.so',
'internal_api': 'openblas',
'num_threads': 20,
'prefix': 'libscipy_openblas',
'threading_layer': 'pthreads',
'user_api': 'blas',
'version': '0.3.31.dev'}]
How does this issue affect you or how did you find it:
This bug was discovered while processing Sentinel-3 OLCI satellite imagery (4091x4865 float32 arrays) using xarray. The xarray library uses the pattern condition |= data == fv in its _apply_mask function (xarray/coding/variables.py, line 231) to replace _FillValue entries with NaN during CF-convention decoding of NetCDF files. Any xarray user reading NetCDF data with fill values on Python 3.14 Linux will hit this segfault if the array exceeds 511x511 elements.
The xarray issue tracking this is: pydata/xarray#11205
The issue was initially difficult to diagnose because the segfault presented as a crash in the Jupyter kernel with no useful error message. It required systematic elimination of hypotheses (file corruption, HDF5 library mismatch, memory exhaustion, IDE overhead) before faulthandler and progressive layer-by-layer unwrapping of xarray's lazy indexing chain identified the root cause as a numpy-level problem.
A possible xarray-side workaround exists (initializing condition as np.zeros(data.shape, dtype=bool) instead of False, or splitting the expression), but the underlying numpy behavior is incorrect regardless of how it is triggered.
Describe the issue:
A segmentation fault occurs when performing a compound in-place boolean OR operation on a temporary comparison result with arrays of 262,144 (2^18) or more elements on Python 3.14.2 on Linux (standard GIL-enabled build, not free-threaded).
The expression condition |= array == scalar, where condition is a Python bool (False) and array is a float32 ndarray, causes a segfault when the array has 512x512 or more elements. Arrays of 511x511 or smaller work without issue.
Critically, separating the expression into two steps avoids the crash entirely:
This suggests the issue is related to the lifetime management of the temporary array produced by array == scalar when it is immediately consumed by the augmented assignment |=. On Python 3.14, CPython's internal reference counting has been reworked (biased reference counting for PEP 703 support, even in the standard GIL-enabled build), which may cause the temporary to be released or its memory to be reused before the |= operation has finished reading from it.
The threshold at exactly 2^18 elements (512x512) points to an internal numpy buffer or dispatch boundary that changes how the operation is executed. Below this threshold, a different code path is taken that does not exhibit the bug.
This bug was originally discovered through xarray, whose _apply_mask function uses this exact pattern to replace _FillValue entries with NaN during CF decoding of NetCDF files. The upstream xarray issue is here: pydata/xarray#11205
Reproduce the code example:
Error message:
Python and NumPy Versions:
Python: 3.14.2 [Clang 21.1.4]
numpy==2.4.2
Runtime Environment:
[{'numpy_version': '2.4.2',
'python': '3.14.2 (main, Jan 27 2026, 23:59:57) [Clang 21.1.4 ]',
'uname': uname_result(system='Linux', node='', release='6.6.87.2-microsoft-standard-WSL2', version='#1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025', machine='x86_64')},
{'simd_extensions': {'baseline': ['X86_V2'],
'found': ['X86_V3'],
'not_found': ['X86_V4', 'AVX512_ICL', 'AVX512_SPR']}},
{'ignore_floating_point_errors_in_matmul': False},
{'architecture': 'Haswell',
'filepath': '/mnt/d/poly/dask_tests/.venv314/lib/python3.14/site-packages/numpy.libs/libscipy_openblas64_-096271d3.so',
'internal_api': 'openblas',
'num_threads': 20,
'prefix': 'libscipy_openblas',
'threading_layer': 'pthreads',
'user_api': 'blas',
'version': '0.3.31.dev'}]
How does this issue affect you or how did you find it:
This bug was discovered while processing Sentinel-3 OLCI satellite imagery (4091x4865 float32 arrays) using xarray. The xarray library uses the pattern condition |= data == fv in its _apply_mask function (xarray/coding/variables.py, line 231) to replace _FillValue entries with NaN during CF-convention decoding of NetCDF files. Any xarray user reading NetCDF data with fill values on Python 3.14 Linux will hit this segfault if the array exceeds 511x511 elements.
The xarray issue tracking this is: pydata/xarray#11205
The issue was initially difficult to diagnose because the segfault presented as a crash in the Jupyter kernel with no useful error message. It required systematic elimination of hypotheses (file corruption, HDF5 library mismatch, memory exhaustion, IDE overhead) before faulthandler and progressive layer-by-layer unwrapping of xarray's lazy indexing chain identified the root cause as a numpy-level problem.
A possible xarray-side workaround exists (initializing condition as np.zeros(data.shape, dtype=bool) instead of False, or splitting the expression), but the underlying numpy behavior is incorrect regardless of how it is triggered.