[Bug] Raise an actionable error when libmlc_llm shared dependencies are missing - #3510
[Bug] Raise an actionable error when libmlc_llm shared dependencies are missing#3510pjdurden wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request improves error handling when loading the MLC LLM library by wrapping ctypes.CDLL in a new load_lib helper, which catches OSError and raises a descriptive RuntimeError if a dependency like libtvm.so is missing. The feedback recommends dynamically determining the platform-specific TVM library name (e.g., .dll or .dylib) in the error message to support Windows and macOS, and removing the redundant NOTES.md file to keep the repository clean.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
Improves the Python-side shared library loader experience by turning an opaque OSError (missing libtvm.so / mismatched mlc-ai install) into a clearer, actionable RuntimeError, matching the existing “actionable error” behavior already used when the MLC library itself cannot be found.
Changes:
- Add
mlc_llm.libinfo.load_lib(path)that wrapsctypes.CDLLand re-raises a descriptiveRuntimeError(with the originalOSErrorchained). - Route
python/mlc_llm/base.pylibrary loading throughlibinfo.load_lib(...)and remove the now-unusedctypesimport. - Add a focused unit test validating both the error-path messaging/cause chaining and the success path.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
python/mlc_llm/libinfo.py |
Adds load_lib() wrapper to provide actionable diagnostics when ctypes.CDLL fails due to missing dependencies like libtvm.so. |
python/mlc_llm/base.py |
Uses the new libinfo.load_lib() helper for consistent error reporting during import-time library loading. |
tests/python/test_libinfo.py |
Adds unit coverage for load_lib() success and missing-dependency error behavior. |
NOTES.md |
Documents the issue context, root cause, and rationale for the loader behavior change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Address review feedback: - remove NOTES.md, which duplicated the PR description - factor the platform specific shared library file name out of find_lib_path into get_lib_file_name and use it so the loader error names tvm.dll / libtvm.dylib / libtvm.so per platform
Issue #3506 —
OSError: libtvm.so: cannot open shared object file1. Root cause
libtvm.sois not part of themlc-llmwheel. It is shipped by the separatemlc-ai/ TVM package. This is deliberate:ci/task/build_lib.shrunsauditwheel repairwith--exclude libtvm --exclude libtvm_runtime --exclude libtvm_ffi ..., so the mlc-llm wheel only bundleslibmlc_llm.so/libmlc_llm_module.so, both of which link againstlibtvm.soat load time.At import,
python/mlc_llm/base.pyloadslibmlc_llm.soviactypes.CDLL. When thematching
mlc-aipackage is missing, incomplete, or its CUDA variant does not match(the exact situation reported for the CUDA 13.0 nightly wheels), the dynamic linker
cannot resolve the
libtvm.sodependency andctypes.CDLLraises a bareOSError: libtvm.so: cannot open shared object file: No such file or directory.The underlying missing-file problem lives in the
mlc-ai/TVM wheel packaging, which isbuilt and published outside this repository — there is no
libtvm.so-producingbuild in mlc-llm to fix here. What is in this repo's control is the loader's behavior:
it turns a resolvable-diagnosis situation ("your
mlc-aiinstall is missing/mismatched")into an opaque
OSErrorthat gives the user no path forward. That unhelpful failure isthe fixable defect.
2. The fix and why
Added
load_lib(path)topython/mlc_llm/libinfo.pyand routedbase.py's loadthrough it. It wraps
ctypes.CDLLand, onOSError, re-raises aRuntimeErrorthat:__cause__and in the message) fordebugging, and
mlc-aipackage must be installed, andfor pip wheels the CUDA variant must match (
mlc-ai-nightly-cuXYZalongsidemlc-llm-nightly-cuXYZ), with a link to the install docs.This mirrors the existing convention in the same file:
find_lib_path(..., optional=False)already raises a descriptive
RuntimeError(with candidate paths) when the mlc-llmlibrary itself is absent. The change extends the same "clear, actionable error" treatment
to the missing-dependency case.
The helper was placed in
libinfo.py(notbase.py) on purpose:libinfo.pyisstandalone — it imports only
os/sys/ctypes, has notvmdependency, and is evenexec-ed directly bysetup.py. That keeps the new logic unit-testable without a fullybuilt
mlc_llm/tvminstall.base.pyno longer referencesctypesdirectly, so thatnow-unused import was removed.
3. Files changed
python/mlc_llm/libinfo.py— addimport ctypes; addload_lib(path)helper.python/mlc_llm/base.py— calllibinfo.load_lib(...)instead ofctypes.CDLL(...);drop the now-unused
import ctypes.tests/python/test_libinfo.py— new focused unit test.4. Risk / uncertainty
load_libreturns exactly whatctypes.CDLLreturned; only the error path changes (a clearerRuntimeErrorin placeof the raw
OSError). Callers of_load_mlc_llm_libdid not catchOSErrorspecifically, so no error-handling contract is broken.
libtvm.so— thatrequires a change in the
mlc-ai/TVM wheel build, which is not in this repository. Thisfix converts the confusing symptom into a self-service diagnostic that points users to
the real remedy; it is a robustness/UX fix, not a repackaging of the upstream wheel.
tvm/mlc_llmare notinstalled in this environment; the test simulates the
OSErrorfromctypes.CDLLinstead (see below).
5. How I verified
python3 -m pytest tests/python/test_libinfo.py -v→ 2 passed. Covers both themissing-dependency error path (asserts the message retains
libtvm.so, mentionsmlc-ai, and chains the originalOSErroras__cause__) and the success path.libinfo.pystill loads two ways: viaimportliband via the exactexec(compile(...))patternsetup.pyuses (so packaging is unaffected).python3 -m py_compileon all three files, andruff checkon them → all checks passed.