Crash report
What happened?
import _interpchannels as c
x = []
while True:
x.append(c.create())
For me on a linux aarch64 VM with 2gb RAM, this reliably reproduces either an abort, or an 'Exception ignored' message.
Modules/_interpchannelsmodule.c:443:
int handle_channel_error(int, PyObject *, int64_t):
Assertion `PyErr_Occurred()' failed.
Aborted
LLDB
The failing assertion occurs in handle_channel_error() with err=-1, while the channel id argument contains -7:
frame #5: handle_channel_error(err=-1, mod=..., cid=-7)
frame #6: channelsmod_create(...)
-7 corresponds to ERR_CHANNEL_MUTEX_INIT.
So the low-level create path correctly identifies the failure, but channelsmod_create() passes the wrong value as the error code.
Root Cause
channel_create() returns a a negative error code on failure, but does not always set an exception. The caller, channelsmod_create converts the error code to -1 which enters a path that asserts an exception has been raised.
|
PyThread_type_lock mutex = PyThread_allocate_lock(); |
|
if (mutex == NULL) { |
|
return ERR_CHANNEL_MUTEX_INIT; |
|
} |
If PyThread_allocate_lock() fails, it returns NULL but does not set a Python exception:
|
PyMutex *lock = (PyMutex *)PyMem_RawMalloc(sizeof(PyMutex)); |
|
if (lock) { |
|
*lock = (PyMutex){0}; |
|
} |
|
|
|
return (PyThread_type_lock)lock; |
Then channel_create correctly returns ERR_CHANNEL_MUTEX_INIT:
|
PyThread_type_lock mutex = PyThread_allocate_lock(); |
|
if (mutex == NULL) { |
|
return ERR_CHANNEL_MUTEX_INIT; |
|
} |
channelsmod_create() sees the error code, but passes -1 to handle_channel_error:
|
int64_t cid = channel_create(&_globals.channels, defaults); |
|
if (cid < 0) { |
|
(void)handle_channel_error(-1, self, cid); |
|
return NULL; |
|
} |
handle_channel_error() then falls through the switch statement because -1 isn't a known error code, resulting in:
|
else { |
|
assert(PyErr_Occurred()); |
|
} |
There is a branch for the -7: ERR_CHANNEL_MUTEX_INIT error code, but this is not hit because of the -1:
|
else if (err == ERR_CHANNEL_MUTEX_INIT) { |
|
PyErr_SetString(state->ChannelError, |
|
"can't initialize mutex for new channel"); |
|
} |
Fix
I'm not an expert in this code, but it seems to me that a reasonable fix here might be to pass cid twice to handle_channel_error(), once as the error code and once as the channel id. This would allow handle_channel_error() to see the actual error code and handle it appropriately:
if (cid < 0) {
(void)handle_channel_error(cid, self, cid);
return NULL;
}
This way, handle_channel_error picks up the -7 error code from cid (already confirmed as < 0) and the switch calls PyErr_SetString, rather than assert(PyErr_Occurred()).
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.16.0a0 (heads/main-dirty:9751e1d, Jun 29 2026, 14:39:58) [Clang 22.1.6 ]
Linked PRs
Crash report
What happened?
For me on a linux aarch64 VM with 2gb RAM, this reliably reproduces either an abort, or an 'Exception ignored' message.
LLDB
The failing assertion occurs in
handle_channel_error()witherr=-1, while the channel id argument contains-7:-7corresponds toERR_CHANNEL_MUTEX_INIT.So the low-level create path correctly identifies the failure, but
channelsmod_create()passes the wrong value as the error code.Root Cause
channel_create()returns a a negative error code on failure, but does not always set an exception. The caller,channelsmod_createconverts the error code to -1 which enters a path that asserts an exception has been raised.cpython/Modules/_interpchannelsmodule.c
Lines 1745 to 1748 in 0a29d84
If
PyThread_allocate_lock()fails, it returnsNULLbut does not set a Python exception:cpython/Python/thread.c
Lines 86 to 91 in 0a29d84
Then channel_create correctly returns ERR_CHANNEL_MUTEX_INIT:
cpython/Modules/_interpchannelsmodule.c
Lines 1745 to 1748 in 0a29d84
channelsmod_create()sees the error code, but passes-1tohandle_channel_error:cpython/Modules/_interpchannelsmodule.c
Lines 2939 to 2943 in 0a29d84
handle_channel_error()then falls through the switch statement because -1 isn't a known error code, resulting in:cpython/Modules/_interpchannelsmodule.c
Lines 442 to 444 in 0a29d84
There is a branch for the
-7: ERR_CHANNEL_MUTEX_INITerror code, but this is not hit because of the-1:cpython/Modules/_interpchannelsmodule.c
Lines 430 to 433 in 0a29d84
Fix
I'm not an expert in this code, but it seems to me that a reasonable fix here might be to pass cid twice to
handle_channel_error(), once as the error code and once as the channel id. This would allowhandle_channel_error()to see the actual error code and handle it appropriately:This way,
handle_channel_errorpicks up the -7 error code fromcid(already confirmed as < 0) and the switch callsPyErr_SetString, rather thanassert(PyErr_Occurred()).CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.16.0a0 (heads/main-dirty:9751e1d, Jun 29 2026, 14:39:58) [Clang 22.1.6 ]
Linked PRs
_interpchannels.create()(GH-152642) #152671_interpchannels.create()(GH-152642) #152672_interpchannels.create()(GH-152642) #152673