WinError 127 loading ggml-base.dll on Windows: bundled libomp140.x86_64.dll is shadowed by a stale copy in System32
Summary
On Windows, import llama_cpp fails with WinError 127 (the specified procedure could not be found) if an older libomp140.x86_64.dll exists in C:\Windows\System32. The wheel ships a compatible libomp140.x86_64.dll right next to ggml-base.dll, but the loader never looks there, so the stale System32 copy wins.
Preloading the bundled DLL by absolute path before importing llama_cpp fixes it completely.
Environment
- Windows 11 x64
- Python 3.13.12 (venv, ComfyUI Desktop)
llama_cpp_python-0.3.43+cu131-cp313-cp313-win_amd64.whl
- Previously working: 0.3.33 from the same source
Error
RuntimeError: Failed to load 'ggml-base' from [.../llama_cpp/lib, .../llama_cpp/bin]
.../llama_cpp/lib/ggml-base.dll: [WinError 127] The specified procedure could not be found.
.../llama_cpp/bin/ggml-base.dll: [WinError 127] The specified procedure could not be found.
Raised from _ctypes_extensions.py:167, via _ggml.py:30.
Note this is error 127, not 126 — the DLL and all of its dependencies were found. One of them was missing an expected export.
Diagnosis
ggml-base.dll imports from libomp140.x86_64.dll. Two copies exist:
| Location |
SHA256 (truncated) |
C:\WINDOWS\SYSTEM32\libomp140.x86_64.dll |
48107D29… |
site-packages\llama_cpp\lib\libomp140.x86_64.dll |
9AB1CB78… |
Different binaries. Note that both report FileVersion 20140926 — LLVM hardcodes that string regardless of release, so version comparison is useless here and actively misleading.
Checking every symbol ggml-base.dll imports against the export table of the bundled copy:
llama_cpp/lib/libomp140.x86_64.dll needs 7, missing 0
llama_cpp/lib/msvcp140.dll needs 4, missing 0
llama_cpp/lib/vcruntime140.dll needs 11, missing 0
The bundled DLLs satisfy ggml-base.dll completely. The wheel is fine — the problem is purely which copy Windows resolves to.
Confirming which file actually gets loaded:
libomp140.x86_64.dll -> C:\WINDOWS\SYSTEM32\libomp140.x86_64.dll
Why the bundled copy loses
Under LoadLibraryExW with no altered-search flags, the search order includes the application directory (python.exe's folder), System32, the Windows directory, and PATH — but not the directory of the DLL being loaded. So when ggml-base.dll resolves its libomp140.x86_64.dll import, llama_cpp/lib is never consulted, even though the file sits right beside it.
Consistent with this: os.add_dll_directory(<llama_cpp/lib>) does not fix the failure, because user directories added that way are only searched when LOAD_LIBRARY_SEARCH_USER_DIRS / LOAD_LIBRARY_SEARCH_DEFAULT_DIRS is in effect.
Workaround
Preloading the bundled libomp by absolute path — which puts a module with that base name in the process, so the loader reuses it instead of searching — resolves it immediately:
import ctypes
ctypes.WinDLL(r"...\site-packages\llama_cpp\lib\libomp140.x86_64.dll")
from llama_cpp.llama_chat_format import Qwen3VLChatHandler # now succeeds
I've made this permanent with a .pth file in site-packages that resolves the path via importlib.util.find_spec("llama_cpp"), so it survives reinstalls.
Suggested fix
In _ggml.py, preload the bundled dependencies from the package directory before loading ggml-base:
import ctypes, os
for _dep in ("libomp140.x86_64.dll",):
for _dir in _base_paths:
_p = os.path.join(_dir, _dep)
if os.path.exists(_p):
try:
ctypes.WinDLL(_p)
except OSError:
pass
break
Alternatively, load ggml-base.dll with winmode=0x00000008 (LOAD_WITH_ALTERED_SEARCH_PATH), which makes the loader treat the DLL's own directory as the search root for its dependencies — though explicit preloading is more predictable.
Either way, the bundled DLLs would then win over whatever happens to be in System32.
WinError 127 loading
ggml-base.dllon Windows: bundledlibomp140.x86_64.dllis shadowed by a stale copy in System32Summary
On Windows,
import llama_cppfails withWinError 127(the specified procedure could not be found) if an olderlibomp140.x86_64.dllexists inC:\Windows\System32. The wheel ships a compatiblelibomp140.x86_64.dllright next toggml-base.dll, but the loader never looks there, so the stale System32 copy wins.Preloading the bundled DLL by absolute path before importing
llama_cppfixes it completely.Environment
llama_cpp_python-0.3.43+cu131-cp313-cp313-win_amd64.whlError
Raised from
_ctypes_extensions.py:167, via_ggml.py:30.Note this is error 127, not 126 — the DLL and all of its dependencies were found. One of them was missing an expected export.
Diagnosis
ggml-base.dllimports fromlibomp140.x86_64.dll. Two copies exist:C:\WINDOWS\SYSTEM32\libomp140.x86_64.dll48107D29…site-packages\llama_cpp\lib\libomp140.x86_64.dll9AB1CB78…Different binaries. Note that both report
FileVersion20140926— LLVM hardcodes that string regardless of release, so version comparison is useless here and actively misleading.Checking every symbol
ggml-base.dllimports against the export table of the bundled copy:The bundled DLLs satisfy
ggml-base.dllcompletely. The wheel is fine — the problem is purely which copy Windows resolves to.Confirming which file actually gets loaded:
Why the bundled copy loses
Under
LoadLibraryExWwith no altered-search flags, the search order includes the application directory (python.exe's folder), System32, the Windows directory, andPATH— but not the directory of the DLL being loaded. So whenggml-base.dllresolves itslibomp140.x86_64.dllimport,llama_cpp/libis never consulted, even though the file sits right beside it.Consistent with this:
os.add_dll_directory(<llama_cpp/lib>)does not fix the failure, because user directories added that way are only searched whenLOAD_LIBRARY_SEARCH_USER_DIRS/LOAD_LIBRARY_SEARCH_DEFAULT_DIRSis in effect.Workaround
Preloading the bundled libomp by absolute path — which puts a module with that base name in the process, so the loader reuses it instead of searching — resolves it immediately:
I've made this permanent with a
.pthfile insite-packagesthat resolves the path viaimportlib.util.find_spec("llama_cpp"), so it survives reinstalls.Suggested fix
In
_ggml.py, preload the bundled dependencies from the package directory before loadingggml-base:Alternatively, load
ggml-base.dllwithwinmode=0x00000008(LOAD_WITH_ALTERED_SEARCH_PATH), which makes the loader treat the DLL's own directory as the search root for its dependencies — though explicit preloading is more predictable.Either way, the bundled DLLs would then win over whatever happens to be in System32.