Repository navigation
Description
Describe the issue:
Brief description of bug
Binary operations on StringDType arrays do not correctly combine the coerce setting from both operands.
When two arrays with different coerce settings are used in an operation such as np.add, the result dtype effectively inherits coerce from the first operand only.
The result should enable coercion only when both input dtypes have coerce=True (same rule as np.promote_types / common_instance).
- Current output
StringDType()
True- Expected output
StringDType(coerce=False)
FalseWhy its incorrect
The result reports coerce=True even though one input has coerce=False. That allows assignments like c[0] = object() to succeed by stringifying the value. With coerce=False preserved, that assignment should raise:
ValueError: StringDType only allows string data when string coercion is disabled.
Note that behavior is order-dependent for ufuncs: np.add(coerce=False, coerce=True) happens to look correct, while np.add(coerce=True, coerce=False) does not which is consistent with only the first descriptor being used.
Debugging hints
np.promote_types already does the right thing both ways, because common_instance in numpy/_core/src/multiarray/stringdtype/dtype.c was fixed recently in #31846 (backport #31856) to use:
dtype1->coerce && dtype2->coerceThe remaining bug is in the binary ufunc path: binary_resolve_descriptors in numpy/_core/src/umath/stringdtype_ufuncs.cpp, which still combines coerce incorrectly:
@@ -248,7 +248,7 @@ binary_resolve_descriptors(...)
{
PyArray_StringDTypeObject *descr1 = (PyArray_StringDTypeObject *)given_descrs[0];
PyArray_StringDTypeObject *descr2 = (PyArray_StringDTypeObject *)given_descrs[1];
- int out_coerce = descr1->coerce && descr1->coerce;
+ int out_coerce = descr1->coerce && descr2->coerce;
PyObject *out_na_object = NULL;So the result dtype depends only on the first input’s coerce flag (the descr1 && descr1 typo), which is why np.add still misbehaves even though type promotion via promote_types is correct.
Applying that one-line change produces the expected result:
StringDType(coerce=False)
Falseand assigning a non-string value correctly raises
ValueError: StringDType only allows string data when string coercion is disabled.
If this is confirmed to be a bug, I'd be happy to contribute a fix
Reproduce the code example:
import numpy as np
from numpy.dtypes import StringDType
a = np.array(["a"], dtype=StringDType(coerce=True))
b = np.array(["b"], dtype=StringDType(coerce=False))
c = np.add(a, b)
print(c.dtype) # currently: StringDType()
print(c.dtype.coerce) # currently: True; expected: False
c[0] = object() # currently succeeds (stringifies); should raise ValueErrorError message:
No exception is raised.
If the resulting dtype preserved coerce=False, assigning a non-string value would raise:
ValueError: StringDType only allows string data when string coercion is disabled.Python and NumPy Versions:
2.6.0.dev0+git20260625.67f37e4
3.14.3 (main, Apr 8 2026, 11:37:06) [Clang 14.0.3 (clang-1403.0.22.14.1)]
Runtime Environment:
[{'numpy_version': '2.6.0.dev0+git20260625.67f37e4',
'python': '3.14.3 (main, Apr 8 2026, 11:37:06) [Clang 14.0.3 '
'(clang-1403.0.22.14.1)]',
'uname': uname_result(system='Darwin', node='192.168.0.100', release='22.6.0', version='Darwin Kernel Version 22.6.0: Wed Jul 5 22:22:52 PDT 2023; root:xnu-8796.141.3~6/RELEASE_ARM64_T8103', machine='arm64')},
{'simd_extensions': {'baseline': ['NEON', 'NEON_FP16', 'NEON_VFPV4', 'ASIMD'],
'found': ['ASIMDHP', 'ASIMDDP'],
'not_found': ['ASIMDFHM']}},
{'ignore_floating_point_errors_in_matmul': True}]
How does this issue affect you or how did you find it:
No response
Activity
Ethan0456 commentedon Jul 4, 2026
Since this is a small fix, I've opened a minimal PR with a regression test here: #31862. Happy to tweak if needed.
TST: cover coerce promotion via test_unset_na_coercion (numpy#31861)
BUG: fix `StringDType` coerce flag in binary ufunc promotion (#31861) (…
BUG: fix `StringDType` coerce flag in binary ufunc promotion (numpy#3…
BUG: fix `StringDType` coerce flag in binary ufunc promotion (#31861) (…
BUG: fix `StringDType` coerce flag in binary ufunc promotion (numpy#3…
BUG: fix `StringDType` coerce flag in binary ufunc promotion (numpy#3…