fix: stop emptying sys.path while loading rxconfig (intermittent ModuleNotFoundError on `reflex run`)

Closed
AL
2mo ago
+71-18

Summary

reflex run intermittently died at backend start with a spurious ModuleNotFoundError for an installed package (socketio, granian, reflex_components_sonner were all observed on the same machine, same venv). It happened on roughly half of dev-server starts while benchmarking a larger app.

Mechanism. reflex_base.config._load_config() cleared sys.path down to [cwd] for the duration of the rxconfig import and restored it afterwards. In reflex run the frontend thread's first get_config() (a fresh RegistrationContext) does that load while the main thread is still inside import reflex.app / the granian import. Python's import system walks sys.path by index, so any first-time import that overlaps the window fails.

Fix. Insert the project root at the front of sys.path once and leave it there (which reflex run already does for the app module via get_app_file()). Restoring the list afterwards is not safe either: removing an entry shifts indices under an in-progress import iterator in another thread. Measured with a stress script (one thread loading the config in a loop, main thread importing socketio 300 times):

loaderfailed imports
main (sys.path.clear() + restore)300 / 300
insert at 0 + restore afterwards18 / 300
insert once, never remove (this PR)0 / 300

The except Exception: retry with the original path fallback is gone with it: the original entries are never removed, so there is nothing to retry with.

Test

test_load_config_keeps_sys_path_intact_for_other_threads: a thread loads the config in a loop while the main thread imports a stdlib module 200 times. Fails on the old loader, passes on this one (mutation-checked).

🤖 Generated with Claude Code