decolua/9router · error

EACCES

EACCES

Error message

Permission denied for port ${LOCAL_PORT}

What it means

The MITM proxy cannot bind LOCAL_PORT because the OS denies the bind (errno EACCES) — the server logs 'Permission denied for port <LOCAL_PORT>' and exits with code 1.

Source

Thrown at src/mitm/server.js:386

    });
    log(`Killed ${pidList.length} process(es) on port ${port}`);
  } catch (e) {
    if (e.status !== 1) throw e;
  }
}

try {
  killPort(LOCAL_PORT);
} catch (e) {
  err(`Cannot kill process on port ${LOCAL_PORT}: ${e.message}`);
  process.exit(1);
}

server.listen(LOCAL_PORT, () => log(`🚀 Server ready on :${LOCAL_PORT}`));

server.on("error", (e) => {
  if (e.code === "EADDRINUSE") err(`Port ${LOCAL_PORT} already in use`);
  else if (e.code === "EACCES") err(`Permission denied for port ${LOCAL_PORT}`);
  else err(e.message);
  process.exit(1);
});

const { removeAllDNSEntriesSync } = require("./dns/dnsConfig");
let isShuttingDown = false;
const shutdown = () => {
  if (isShuttingDown) return;
  isShuttingDown = true;
  // Strip tool hosts from /etc/hosts so other apps aren't broken after exit
  removeAllDNSEntriesSync();
  const forceExit = setTimeout(() => process.exit(0), 1500);
  server.close(() => { clearTimeout(forceExit); process.exit(0); });
};
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
if (process.platform === "win32") process.on("SIGBREAK", shutdown);

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Set LOCAL_PORT to an unprivileged port (>1024, e.g. 20129) and point the tool's proxy/DNS override at it instead of 443.
  2. Grant the Node binary the capability: sudo setcap 'cap_net_bind_service=+ep' $(which node) (Linux).
  3. Run with elevated privileges only if acceptable (sudo) — not recommended for a local proxy.
  4. Check SELinux/AppArmor/sandbox policies if the port is unprivileged but still denied.

Example fix

// before
LOCAL_PORT=443 → EACCES (unprivileged)
// after
LOCAL_PORT=20129  # and map the upstream host to 127.0.0.1:20129 via the mitm DNS config
Defensive patterns

Strategy: validation

Validate before calling

const LOCAL_PORT = Number(process.env.MITM_LOCAL_PORT || 20129);
if (LOCAL_PORT < 1024 && process.getuid && process.getuid() !== 0) {
  console.error(`Port ${LOCAL_PORT} is privileged — use a port > 1024 or grant cap_net_bind_service`);
  process.exit(1);
}

Type guard

function isPrivilegedPort(p) { return Number.isInteger(p) && p > 0 && p < 1024; }

Try / catch

server.on('error', (e) => {
  if (e.code === 'EACCES') {
    err(`Permission denied for port ${LOCAL_PORT} — use an unprivileged port (>1024)`);
    process.exit(1);
  }
});

Prevention

When it happens

Trigger: server.listen(LOCAL_PORT) with LOCAL_PORT < 1024 on Linux/macOS without root/CAP_NET_BIND_SERVICE, or on a port blocked by local security policy (SELinux, sandboxed environment).

Common situations: Configured the MITM port to 80/443 to impersonate the real upstream without elevated privileges; running inside a container/sandbox that forbids binding privileged ports; firewall policy blocking the bind.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/f86b09b868e0ae9e. Report an issue: GitHub.