#158373·Python

os.exec*e / os.spawn*e with env crash on Windows non-MSVC builds (MINGW/clang)

Author: cubershipyardCreated Sep 28, 2026Updated Sep 29, 2026
Labelstype-bugOS-windowsextension-modules

Bug

On Windows builds compiled with a non-MSVC toolchain (MINGW / clang, as shipped by MSYS2), the os.exec*e and os.spawn*e variants crash with an access violation whenever an env argument is passed. The non-env variants are fine.

This is the same defect as gh-67650 ("All os.exec*e variants crash on Windows", formerly bpo-23462); it was closed as fixed in 3.6, but the fix does not cover MINGW/clang builds, which still crash.

Reproducer

python
import os, sys
exe = os.path.join(os.path.dirname(sys.executable), "python.exe")
os.execve(exe, [exe, "-c", "pass"], {"PATH": os.environ["PATH"]})
$ python repro.py
Windows fatal exception: access violation
Current thread 0x000045cc (most recent call first):
  File "repro.py", line 4 in <module>
$ echo $?
139

Measured (MINGW clang64 python 3.14.7, 20 runs each)

Call Success
os.execv(exe, argv) (no env) 20/20
os.spawnv(P_WAIT, exe, argv) (no env) 20/20
os.execve(exe, argv, env) 0/20
os.spawnve(P_WAIT, exe, argv, env) 0/20

Root cause

HAVE_WEXECV / HAVE_WSPAWNV are defined in Modules/posixmodule.c only for the Microsoft compiler:

c
#elif defined( _MSC_VER)
  /* Microsoft compiler */
  ...
#  if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM)
#    define HAVE_WEXECV     1
#    define HAVE_WSPAWNV    1
#  endif
#endif

MINGW/clang does not define _MSC_VER, so HAVE_WEXECV is absent and the code falls into the narrow branch. On Windows path->narrow is NULL (the path is converted to wide), so the call faults (Modules/posixmodule.c, v3.14.0):

c
/* execv, line 7024 */
#ifdef HAVE_WEXECV
    _wexecv(path->wide, argvlist);
#else
    execv(path->narrow, argvlist);          /* path->narrow == NULL -> crash */
#endif

/* execve, line 7112 */
#ifdef HAVE_WEXECV
        _wexecve(path->wide, argvlist, envlist);
#else
        execve(path->narrow, argvlist, envlist);   /* crash */
#endif

Evidence the wide functions are available

MSYS2 pyconfig.h has HAVE_EXECV but not HAVE_WEXECV:

$ grep -n "WEXECV\|HAVE_EXECV" /clang64/include/python3.14/pyconfig.h
364:#define HAVE_EXECV 1          # <- no HAVE_WEXECV

Yet msvcrt exports all four wide calls:

python
>>> import ctypes
>>> for n in ("_wexecv", "_wexecve", "_wspawnv", "_wspawnve"):
...     print(n, hasattr(ctypes.CDLL("msvcrt.dll"), n))
_wexecv True
_wexecve True
_wspawnv True
_wspawnve True

So the CRT supports the fix; only the compile-time gate is wrong for this toolchain.

Suggested fix

Detect the wide CRT functions for non-MSVC Windows toolchains as well, instead of gating HAVE_WEXECV / HAVE_WSPAWNV on _MSC_VER. If detection is awkward in pyconfig, posixmodule.c could define them for MS_WINDOWS builds whose CRT provides _wexecv/_wexecve/_wspawnv/_wspawnve.

Impact

Hard crash for any MINGW/clang Windows Python (MSYS2 clang64/ucrt64/mingw64) code that uses os.execve/execvpe/spawnve. No pure-Python workaround for exec*e (they replace the process); subprocess with env is a workaround.

Related: the same issue was reported downstream to MSYS2 — https://github.com/msys2/MINGW-packages/issues/32022