Problem
The ensureCursorProxyServer function in plugin.ts performs a fetch("http://127.0.0.1:32124/health") with no timeout to check if a cursor proxy is already running. On systems where TCP SYN packets to unused ports on 127.0.0.1 are silently dropped (rather than returning ECONNREFUSED), this fetch hangs until the OS-level TCP timeout fires (~120–189s depending on tcp_syn_retries).
This effectively blocks plugin initialization for ~2 minutes before falling through to server.listen(), which then works fine.
Steps to Reproduce
- Install @rama_nigg/open-cursor (tested with v2.4.4)
- Ensure no existing cursor-acp proxy is running on port 32124
- Launch opencode (or any host that loads the plugin)
- Observe ~2 minute delay during plugin initialization
Expected vs. Actual Behavior
- Expected: Health check returns quickly with an error (ECONNREFUSED or fetch error), and the plugin falls through to starting its own proxy server.
- Actual: fetch() hangs for ~120–189s with no response, delaying entire plugin startup. Eventually the host's default HTTP timeout or OS TCP timeout fires and the plugin continues.
Technical Details
- The hanging call is at plugin.ts:1127 inside ensureCursorProxyServer (the REUSE_EXISTING_PROXY branch, default true)
- The fetch() has no timeout option set — no AbortSignal, no signal parameter
- The .catch(() => null) on the fetch only handles rejected promises, not hanging connections
- Works Around: set env with CURSOR_ACP_REUSE_EXISTING_PROXY=false
skips the health check
- Root cause: bind() to 127.0.0.1:32124 works fine; the issue is specifically connect() to a non-listening port on 127.0.0.1, which on some system configurations silently drops the SYN instead of sending RST
Suggested Fix
Add a short timeout (e.g. 2–3 seconds) to the health check fetch to prevent hanging:
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 3000);
try {
const res = await fetch(`http://${host}:${port}/health`, {
signal: controller.signal,
}).catch(() => null);
// ...
} finally {
clearTimeout(timeout);
}
This ensures the health check fails fast if no proxy is running, regardless of the system's TCP behavior on 127.0.0.1.
Problem
The ensureCursorProxyServer function in plugin.ts performs a fetch("http://127.0.0.1:32124/health") with no timeout to check if a cursor proxy is already running. On systems where TCP SYN packets to unused ports on 127.0.0.1 are silently dropped (rather than returning ECONNREFUSED), this fetch hangs until the OS-level TCP timeout fires (~120–189s depending on tcp_syn_retries).
This effectively blocks plugin initialization for ~2 minutes before falling through to server.listen(), which then works fine.
Steps to Reproduce
Expected vs. Actual Behavior
Technical Details
skips the health check
Suggested Fix
Add a short timeout (e.g. 2–3 seconds) to the health check fetch to prevent hanging:
This ensures the health check fails fast if no proxy is running, regardless of the system's TCP behavior on 127.0.0.1.