FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

BUG: Segfault in compound in-place boolean operation `condition |= array == scalar` on arrays >= 512x512 on Python 3.14 Linux · Issue #30899 · numpy/numpy · GitHub

/ numpy Public

BUG: Segfault in compound in-place boolean operation condition |= array == scalar on arrays >= 512x512 on Python 3.14 Linux #30899

Description

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


    Back | FazBrowse Home | New Git URL