{"record":{"id":"7ed7f32773393cc8","repo":"decolua/9router","slug":"mitm-server-is-already-running","errorCode":null,"errorMessage":"MITM server is already running","messagePattern":"MITM server is already running","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/mitm/manager.js","lineNumber":490,"sourceCode":"  if (!serverProcess || serverProcess.killed) {\n    try {\n      if (fs.existsSync(PID_FILE)) {\n        const savedPid = parseInt(fs.readFileSync(PID_FILE, \"utf-8\").trim(), 10);\n        if (savedPid && isProcessAlive(savedPid)) {\n          serverPid = savedPid;\n          log(`♻️ Reusing existing process (PID: ${savedPid})`);\n          await saveMitmSettings(true, sudoPassword);\n          if (sudoPassword) setCachedPassword(sudoPassword);\n          return { running: true, pid: savedPid };\n        } else {\n          fs.unlinkSync(PID_FILE);\n        }\n      }\n    } catch { /* ignore */ }\n  }\n\n  if (serverProcess && !serverProcess.killed) {\n    throw new Error(\"MITM server is already running\");\n  }\n\n  // Atomically claim lock to prevent concurrent startServer across processes.\n  // O_EXCL (flag: \"wx\") fails with EEXIST if the file already exists.\n  try {\n    fs.writeFileSync(LOCK_FILE, String(process.pid), { flag: \"wx\" });\n  } catch (e) {\n    if (e.code === \"EEXIST\") {\n      let stale = false;\n      try {\n        const pid = parseInt(fs.readFileSync(LOCK_FILE, \"utf-8\").trim(), 10);\n        stale = !pid || !isProcessAlive(pid);\n      } catch { stale = true; } // unreadable lock → treat as stale\n      if (!stale) throw new Error(\"MITM server is already starting (lock contention)\");\n      try { fs.unlinkSync(LOCK_FILE); } catch { /* ignore */ }\n      fs.writeFileSync(LOCK_FILE, String(process.pid), { flag: \"wx\" });\n    } else throw e;\n  }","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/mitm/manager.js#L472-L508","documentation":"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.)","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait startServer(apiKey, sudoPassword);\nawait startServer(apiKey, sudoPassword); // throws\n// after\nconst status = mitmManager.getStatus?.() ?? {};\nif (!status.running) {\n  await startServer(apiKey, sudoPassword);\n}","handlingStrategy":"try-catch","validationCode":"const pidFile = PID_FILE;\nconst fs = require('fs');\nlet running = false;\ntry {\n  const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);\n  running = pid && process.kill(pid, 0) !== undefined;\n} catch { running = false; }\nif (!running) await startServer(apiKey, sudoPassword);","typeGuard":null,"tryCatchPattern":"try {\n  await startServer(apiKey, sudoPassword);\n} catch (e) {\n  if (e.message === 'MITM server is already running') return { running: true }; // idempotent start\n  if (e.message.includes('already starting')) await waitForLockRelease();\n  else throw e;\n}","preventionTips":["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"],"tags":["mitm","lifecycle","double-start","port-443"],"backgroundTag":"server-already-running","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}