sgl-project/sglang · error · RuntimeError
cannot force-build {python_module} after it has been importe
Error message
cannot force-build {python_module} after it has been imported; start a new Python process What it means
In 'force' mode the loader must rebuild and re-import the extension, but Python cannot cleanly replace an already-imported native module. If the module is already in sys.modules, force-building is refused with this RuntimeError; the only remedy is a fresh process.
Source
Thrown at python/sglang/srt/rust_extensions/loader.py:97
``auto`` prefers a module bundled in the installed wheel, then a cached
local build, and finally Cargo. ``never`` permits the first two but never
invokes Cargo. ``force`` rebuilds from source and replaces the cache entry.
``mode`` defaults to ``SGLANG_RUST_BUILD_MODE``.
"""
if mode is None:
mode = envs.SGLANG_RUST_BUILD_MODE.get()
if mode not in ("auto", "never", "force"):
raise ValueError(
f"invalid Rust extension build mode {mode!r}; expected auto, never, or force"
)
if mode != "force":
module = _import_bundled_extension(python_module)
if module is not None:
return module
elif python_module in sys.modules:
raise RuntimeError(
f"cannot force-build {python_module} after it has been imported; "
"start a new Python process"
)
if workspace is None:
workspace = _RUST_WORKSPACE
crate = _discover_crate(workspace, python_module)
context = _build_context(crate)
cache_root = _cache_root(cache_dir)
extension_path = _cached_extension_path(cache_root, crate, context.fingerprint)
lock_path = (
cache_root / "locks" / f"{crate.package}-{context.target_fingerprint}.lock"
)
with _filesystem_lock(lock_path):
if mode != "force" and extension_path.is_file():
return _load_extension_from_path(crate.python_module, extension_path)
View on GitHub (pinned to 0132848349)
Solutions
- Run the force build in a fresh Python process (subprocess or new test session) so the rebuilt cache is produced outside your interpreter
- Use force at most once per module per process — the first load in a process works; subsequent force loads fail
- If you just need the existing build, call with the default/auto mode, which returns the already-imported or cached module
Example fix
# before mod = load_rust_extension(crate, mode="force") # second call in same process # after import subprocess, sys subprocess.run([sys.executable, "-c", "from sglang.srt.rust_extensions.loader import load_rust_extension; load_rust_extension(CRATE, mode='force')"], check=True) mod = load_rust_extension(CRATE) # auto: picks up the fresh cache
Defensive patterns
Strategy: fallback
Validate before calling
import sys mode = "force" if crate.python_module not in sys.modules else "auto" module = load_rust_extension(crate, mode=mode)
Try / catch
try:
module = load_rust_extension(crate, mode="force")
except RuntimeError:
module = load_rust_extension(crate) # fall back to cached/imported build Prevention
- Run force builds in a subprocess (fresh interpreter) to avoid import-cache staleness
- Only pass force on the first load in a process
- Isolate Rust-rebuilding tests into separate pytest invocations
When it happens
Trigger: Calling load_rust_extension(mode="force") after anything (e.g. an earlier auto-mode load) already imported the crate — the loader checks python_module in sys.modules and raises; long-lived test sessions that try to force-rebuild between tests.
Common situations: Dev loops that edit Rust source and re-run the loader in the same interpreter (notebooks, a single pytest session); wrappers that unconditionally pass force on every call.
Related errors
- invalid Rust extension build mode {mode!r}; expected auto, n
- {crate.python_module} is not bundled or cached, and Rust ext
- Unknown serve backend {name!r}. Available values: {available
- Multiple distributions register serve backend {name!r}: {pro
- Failed to load serve backend {name!r} from {self._entry_poin
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/a1506f4398e73051.
Report an issue: GitHub.