decolua/9router · error · Error
MITM server is already running
Error message
MITM server is already running
What it means
startServer() in the MITM manager first tries to reuse an existing live process via the PID file; if an in-memory serverProcess still exists and is not killed, it throws 'MITM server is already running' instead of starting a second instance. Starting a second MITM server would fail to bind port 443 and corrupt state, so the library treats double-start as an error. (A separate 'already starting' error covers lock contention during startup.)
Source
Thrown at src/mitm/manager.js:490
if (!serverProcess || serverProcess.killed) {
try {
if (fs.existsSync(PID_FILE)) {
const savedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
if (savedPid && isProcessAlive(savedPid)) {
serverPid = savedPid;
log(`♻️ Reusing existing process (PID: ${savedPid})`);
await saveMitmSettings(true, sudoPassword);
if (sudoPassword) setCachedPassword(sudoPassword);
return { running: true, pid: savedPid };
} else {
fs.unlinkSync(PID_FILE);
}
}
} catch { /* ignore */ }
}
if (serverProcess && !serverProcess.killed) {
throw new Error("MITM server is already running");
}
// Atomically claim lock to prevent concurrent startServer across processes.
// O_EXCL (flag: "wx") fails with EEXIST if the file already exists.
try {
fs.writeFileSync(LOCK_FILE, String(process.pid), { flag: "wx" });
} catch (e) {
if (e.code === "EEXIST") {
let stale = false;
try {
const pid = parseInt(fs.readFileSync(LOCK_FILE, "utf-8").trim(), 10);
stale = !pid || !isProcessAlive(pid);
} catch { stale = true; } // unreadable lock → treat as stale
if (!stale) throw new Error("MITM server is already starting (lock contention)");
try { fs.unlinkSync(LOCK_FILE); } catch { /* ignore */ }
fs.writeFileSync(LOCK_FILE, String(process.pid), { flag: "wx" });
} else throw e;
}View on GitHub (pinned to 90b52e06ff)
Solutions
- Check running state first (e.g. getStatus/isRunning or the PID file) and skip startServer if it returns running
- Serialize start calls — await the previous startServer promise or debounce/guard the start action in the UI
- Call stopServer() (or stopMitm) before startServer if a restart is intended
- If the running instance is stale/unwanted, kill it (stopServer or kill the PID) then start again
Example fix
// before
await startServer(apiKey, sudoPassword);
await startServer(apiKey, sudoPassword); // throws
// after
const status = mitmManager.getStatus?.() ?? {};
if (!status.running) {
await startServer(apiKey, sudoPassword);
} Defensive patterns
Strategy: try-catch
Validate before calling
const pidFile = PID_FILE;
const fs = require('fs');
let running = false;
try {
const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
running = pid && process.kill(pid, 0) !== undefined;
} catch { running = false; }
if (!running) await startServer(apiKey, sudoPassword); Try / catch
try {
await startServer(apiKey, sudoPassword);
} catch (e) {
if (e.message === 'MITM server is already running') return { running: true }; // idempotent start
if (e.message.includes('already starting')) await waitForLockRelease();
else throw e;
} Prevention
- Await every startServer promise; never fire-and-forget
- Debounce/disable the start control while a start is in flight
- Call stopServer before intentional restarts
- Treat 'already running' as success for idempotent start flows
When it happens
Trigger: Calling startServer(apiKey, sudoPassword) when a previous startServer in the same process left serverProcess alive and running; invoking start twice from UI double-clicks or concurrent event handlers before the first call resolved; calling startServer after startMitm already launched the server component.
Common situations: Dashboard 'Start' button clicked twice rapidly; a script calling startServer without awaiting the first promise; restart logic that checks status asynchronously and races with an existing server; hot-reload environments where the module-level serverProcess persists across invocations.
Related errors
- MITM server is not running. Start the server first.
- Machine ID is required for Cursor API
- http2 module not available
- HTTP/2 is required for Cursor AgentService (endpoint is h2-o
- Cursor AgentService endpoint is not configured
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/7ed7f32773393cc8.
Report an issue: GitHub.