smithery/Gambitnl

nodejs-port-cleanup

Automatically terminate conflicting port processes on Windows/Unix before starting Node.js servers to avoid EADDRINUSE errors.

Installation

$ npx skills add smithery/Gambitnl --skill nodejs-port-cleanup

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/Gambitnl.

npx skills add smithery/Gambitnl

Browse all from smithery/Gambitnl

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Version1.0.0
Declared agents claude-code

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 6,126 B
  • docs SUMMARY.md 353 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Node.js Port Cleanup on Server Start

Problem

When restarting a development server, you often encounter EADDRINUSE: address already in use errors because a previous instance is still running. This is especially common when:

  • The server was started in a background process that wasn't properly terminated
  • The terminal was closed without stopping the server
  • The server crashed but the process didn't exit cleanly

Context / Trigger Conditions

  • Error: Error: listen EADDRINUSE: address already in use :::PORT
  • Attempting to start a dev server that was recently running
  • Background tasks holding ports after their parent process ended
  • IDE or terminal sessions that didn't clean up properly

Solution

Add this function to your Node.js server startup script:

import { execSync } from 'child_process';

/**
 * Kills any existing process using the specified port.
 * This prevents EADDRINUSE errors when restarting the server.
 * Works on Windows by using netstat to find the PID and taskkill to terminate it.
 * Works on Unix/Mac by using lsof to find and kill the process.
 */
function killProcessOnPort(port: number): void {
  const isWindows = process.platform === 'win32';

  try {
    if (isWindows) {
      // Use netstat to find the PID of the process listening on this port
      // netstat output format: "  TCP    0.0.0.0:3847    0.0.0.0:0    LISTENING    12345"
      const netstatOutput = execSync(`netstat -ano | findstr :${port}`, {
        encoding: 'utf-8',
        stdio: ['pipe', 'pipe', 'pipe'], // Suppress stderr
      });

      // Parse each line to extract PIDs of listening processes
      const lines = netstatOutput.trim().split('\n');
      const pids = new Set<string>();

      for (const line of lines) {
        // Only target LISTENING connections on our exact port
        if (line.includes('LISTENING')) {
          // Split by whitespace and get the last column (PID)
          const parts = line.trim().split(/\s+/);
          const pid = parts[parts.length - 1];
          if (pid && /^\d+$/.test(pid)) {
            pids.add(pid);
          }
        }
      }

      // Kill each process found
      for (const pid of pids) {
        try {
          execSync(`taskkill /PID ${pid} /F`, {
            encoding: 'utf-8',
            stdio: ['pipe', 'pipe', 'pipe'],
          });
          console.log(`Killed existing process on port ${port} (PID: ${pid})`);
        } catch {
          // Process may have already exited, ignore
        }
      }
    } else {
      // Unix/Mac: use lsof to find and kill the process
      const lsofOutput = execSync(`lsof -ti:${port}`, {
        encoding: 'utf-8',
        stdio: ['pipe', 'pipe', 'pipe'],
      });

      const pids = lsofOutput.trim().split('\n').filter(Boolean);
      for (const pid of pids) {
        try {
          execSync(`kill -9 ${pid}`, { stdio: ['pipe', 'pipe', 'pipe'] });
          console.log(`Killed existing process on port ${port} (PID: ${pid})`);
        } catch {
          // Process may have already exited, ignore
        }
      }
    }
  } catch {
    // No process found on port - this is fine, nothing to kill
  }
}

// Call before creating the server
const PORT = 3000;
killProcessOnPort(PORT);

// Then create and start your server as normal
const server = http.createServer(/* ... */);
server.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}`);
});

Verification

When the function successfully kills a process, you'll see:

Killed existing process on port 3847 (PID: 12345)

If no process was found (port was free), the function silently proceeds.

Example

Before this fix:

> npx tsx scripts/my-server.ts
Error: listen EADDRINUSE: address already in use :::3847

After adding killProcessOnPort():

> npx tsx scripts/my-server.ts
Killed existing process on port 3847 (PID: 39592)

Server running at http://localhost:3847

Notes

  • Windows: Uses netstat -ano to find PIDs and taskkill /F to force-kill
  • Unix/Mac: Uses lsof -ti:PORT to find PIDs and kill -9 to force-kill
  • The function silently handles cases where no process is found (empty catch block)
  • Only targets processes in LISTENING state to avoid killing unrelated connections
  • Force-kill (/F on Windows, -9 on Unix) ensures stubborn processes are terminated
  • Consider adding a small delay after killing if the port doesn't release immediately

Alternative: Manual Cleanup Commands

If you need to manually kill a process on a port:

Windows:

# Find the PID
netstat -ano | findstr :3847

# Kill it
taskkill /PID <pid> /F
# Or via PowerShell
Stop-Process -Id <pid> -Force

Unix/Mac:

# Find and kill in one command
lsof -ti:3847 | xargs kill -9

References

Completion Criteria

Before concluding any port cleanup task, you must satisfy the following checklist:

  1. Verify Termination: Check that the process using the target port is terminated successfully using netstat -ano | findstr :PORT (Windows) or lsof -ti:PORT (Unix/Mac).
  2. Server Spawning: Confirm that the dev server can start up on the target port cleanly without raising EADDRINUSE.
  3. Cross-Platform Compatibility: Ensure the implementation handles both Windows and Unix OS environments appropriately.
  4. Log Verification: Confirm that if a process was terminated, a success message was logged outputting the port and process ID (PID).
  5. No Collateral Damage: Ensure only connections matching the exact port in the LISTENING state are targeted.