Category: bug
Description
apx dev start enters an infinite OpenAPI regeneration loop on WSL2 (Linux 5.15.167.4-microsoft-standard-WSL2). The loop fires "Python change
detected, regenerating OpenAPI…" every ~2 seconds continuously, causing constant Vite HMR updates that break interactive UI components (e.g.,
editable table cells lose focus on re-render). The apx process consumes ~22% CPU during the loop.
Root Cause
The file watcher (crates/core/src/dev/watcher.rs) registers inotify watches with mask 0x3EE, which includes IN_OPEN (0x020). This means any file
opened for reading triggers an event.
The OpenAPI regeneration subprocess (uv run apx __generate_openapi) opens files across the project tree to build the schema. These read-only opens
generate IN_OPEN events that apx interprets as "Python changes", triggering another regeneration — an infinite loop.
Evidence
- inotifywait -e open captured 2,700 OPEN events in 8 seconds during the loop — all from the regeneration subprocess reading project files
- inotifywait -e modify,create,delete shows zero actual file changes after initial startup (when api.ts is read-only), yet the loop continues
— proving IN_OPEN events are the trigger
- The inotify mask from /proc/pid/fdinfo/fd is 0x3EE = IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | IN_OPEN | IN_MOVED_FROM | IN_MOVED_TO |
IN_CREATE | IN_DELETE
- apx registers 5,299 inotify watches across the project tree
Workaround
An LD_PRELOAD shim that strips IN_OPEN and IN_ATTRIB from inotify_add_watch calls completely fixes the issue. With the fix:
- No regeneration loop after startup
- File change detection still works correctly (tested: editing router.py triggers exactly one regeneration cycle, then stops)
// fix_inotify.c — compile with: gcc -shared -fPIC -o fix_inotify.so fix_inotify.c -ldl
#define _GNU_SOURCE
#include <stddef.h>
#include <dlfcn.h>
#include <sys/inotify.h>
int inotify_add_watch(int fd, const char *pathname, uint32_t mask) {
typedef int (*orig_fn)(int, const char *, uint32_t);
static orig_fn orig = NULL;
if (!orig) orig = (orig_fn)dlsym(RTLD_NEXT, "inotify_add_watch");
mask &= ~(IN_OPEN | IN_ATTRIB);
return orig(fd, pathname, mask);
}
Usage: LD_PRELOAD=./fix_inotify.so apx dev start
Suggested Fix
Remove IN_OPEN (0x020) from the inotify watch mask in crates/core/src/dev/watcher.rs. Consider also removing IN_ATTRIB (0x004). The corrected mask
0x3CA (IN_MODIFY | IN_CLOSE_WRITE | IN_MOVED_FROM | IN_MOVED_TO | IN_CREATE | IN_DELETE) is sufficient for detecting file modifications without
triggering on read-only access.
Environment
- apx: 0.3.8
- OS: WSL2 (Linux 5.15.167.4-microsoft-standard-WSL2)
- Python: 3.11
- notify crate: 0.11.0 (inotify backend)
Category: bug
Description
apx dev start enters an infinite OpenAPI regeneration loop on WSL2 (Linux 5.15.167.4-microsoft-standard-WSL2). The loop fires "Python change
detected, regenerating OpenAPI…" every ~2 seconds continuously, causing constant Vite HMR updates that break interactive UI components (e.g.,
editable table cells lose focus on re-render). The apx process consumes ~22% CPU during the loop.
Root Cause
The file watcher (crates/core/src/dev/watcher.rs) registers inotify watches with mask 0x3EE, which includes IN_OPEN (0x020). This means any file
opened for reading triggers an event.
The OpenAPI regeneration subprocess (uv run apx __generate_openapi) opens files across the project tree to build the schema. These read-only opens
generate IN_OPEN events that apx interprets as "Python changes", triggering another regeneration — an infinite loop.
Evidence
— proving IN_OPEN events are the trigger
IN_CREATE | IN_DELETE
Workaround
An LD_PRELOAD shim that strips IN_OPEN and IN_ATTRIB from inotify_add_watch calls completely fixes the issue. With the fix: