{"record":{"id":"a1506f4398e73051","repo":"sgl-project/sglang","slug":"cannot-force-build-python-module-after-it-has-be","errorCode":null,"errorMessage":"cannot force-build {python_module} after it has been imported; start a new Python process","messagePattern":"cannot force-build (.+?) after it has been imported; start a new Python process","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/rust_extensions/loader.py","lineNumber":97,"sourceCode":"\n    ``auto`` prefers a module bundled in the installed wheel, then a cached\n    local build, and finally Cargo. ``never`` permits the first two but never\n    invokes Cargo. ``force`` rebuilds from source and replaces the cache entry.\n    ``mode`` defaults to ``SGLANG_RUST_BUILD_MODE``.\n    \"\"\"\n    if mode is None:\n        mode = envs.SGLANG_RUST_BUILD_MODE.get()\n    if mode not in (\"auto\", \"never\", \"force\"):\n        raise ValueError(\n            f\"invalid Rust extension build mode {mode!r}; expected auto, never, or force\"\n        )\n\n    if mode != \"force\":\n        module = _import_bundled_extension(python_module)\n        if module is not None:\n            return module\n    elif python_module in sys.modules:\n        raise RuntimeError(\n            f\"cannot force-build {python_module} after it has been imported; \"\n            \"start a new Python process\"\n        )\n\n    if workspace is None:\n        workspace = _RUST_WORKSPACE\n    crate = _discover_crate(workspace, python_module)\n    context = _build_context(crate)\n    cache_root = _cache_root(cache_dir)\n    extension_path = _cached_extension_path(cache_root, crate, context.fingerprint)\n    lock_path = (\n        cache_root / \"locks\" / f\"{crate.package}-{context.target_fingerprint}.lock\"\n    )\n\n    with _filesystem_lock(lock_path):\n        if mode != \"force\" and extension_path.is_file():\n            return _load_extension_from_path(crate.python_module, extension_path)\n","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/rust_extensions/loader.py#L79-L115","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nmod = load_rust_extension(crate, mode=\"force\")  # second call in same process\n# after\nimport subprocess, sys\nsubprocess.run([sys.executable, \"-c\", \"from sglang.srt.rust_extensions.loader import load_rust_extension; load_rust_extension(CRATE, mode='force')\"], check=True)\nmod = load_rust_extension(CRATE)  # auto: picks up the fresh cache","handlingStrategy":"fallback","validationCode":"import sys\nmode = \"force\" if crate.python_module not in sys.modules else \"auto\"\nmodule = load_rust_extension(crate, mode=mode)","typeGuard":null,"tryCatchPattern":"try:\n    module = load_rust_extension(crate, mode=\"force\")\nexcept RuntimeError:\n    module = load_rust_extension(crate)  # fall back to cached/imported build","preventionTips":["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"],"tags":["rust-extension","import-cache","force-rebuild","python","sglang"],"backgroundTag":"module-already-imported","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}