macOS: Serial Port Busy — Find What’s Holding `/dev/cu.*` and Free It

← Back to Blog

macOS: Serial Port Busy — Find What’s Holding /dev/cu.* and Free It

If you use serial console ports often, you’ll eventually hit the classic problem: the adapter shows up, the port is there, but your terminal refuses to open it. You’ll see errors like “Resource busy,” “Device is busy,” or it just fails to connect.

Most of the time nothing is “broken.” The port is already open in another process—sometimes it’s your own forgotten screen session, sometimes a GUI terminal app, sometimes a Python script that’s still running, or a background logger you started earlier.

This post shows a quick, calm way to figure out what’s holding the port, free it, and avoid the same issue next time.

If you do this kind of work regularly (especially across lots of devices), take a look at clideck.com. We’re building practical serial/console guides and tools for people who want things to work the same way every time—without wasting an evening on small but annoying problems.


Quick fix

  1. List ports and copy the one you’re using:
ls -1 /dev/cu.* | head
  1. See what process is holding the port (replace with your port name):
lsof /dev/cu.usbserial-0001
  1. If it’s screen, close it cleanly:
screen -ls
screen -r <ID>
# then inside screen: Ctrl+A, then K, then y
  1. If the process is stuck, end it:
kill <PID>

If that doesn’t work:

kill -9 <PID>
  1. Confirm the port is free:
lsof /dev/cu.usbserial-0001

No output usually means you’re good.


Why this happens

On macOS, a serial port is a device file (like /dev/cu.* and /dev/tty.*). Once one process opens it, a second process typically can’t open the same port at the same time.

Common culprits:

  • a forgotten screen session (maybe in another Terminal window)
  • SecureCRT / CoolTerm / Serial / any GUI terminal app
  • a Python script (pyserial) still running
  • a background logging command you started and forgot about

Step 1: Make sure you’re opening the right port

On macOS, you usually want /dev/cu.*, not /dev/tty.*.

List them:

ls -1 /dev/cu.*

If you have multiple adapters plugged in, it’s easy to grab the wrong one. Always copy/paste the full device name.


Step 2: Identify what’s holding the port

The simplest check:

lsof /dev/cu.usbserial-0001

If you’re not sure which exact port name you need, you can scan all open serial devices:

lsof | grep -E '/dev/cu\.' | head -n 50

In the lsof output, pay attention to:

  • COMMAND (what program it is)
  • PID (the process ID you can kill)
  • USER (useful if it’s not your session)

Step 3: The most common case — it’s screen

List active screen sessions

screen -ls

If you see entries like:

  • 12345.tty.usbserial-0001 (Attached)
  • 67890.tty.usbserial-0001 (Detached)

…that’s usually your answer.

If the session is “Attached”

Sometimes it’s open in another window and you forgot. You can:

  • go to that window and exit normally, or
  • take control of the session:
screen -r <ID>

If it says it’s already attached, use:

screen -d -r <ID>

Close screen the right way so the port is released

Inside screen:

  • Ctrl+A, then K, then y

Then confirm:

lsof /dev/cu.usbserial-0001

Step 4: If it’s not screen

You might see python, node, cu, minicom, CoolTerm, Serial, etc.

Try a normal stop first

kill <PID>

Wait a second, then re-check:

lsof /dev/cu.usbserial-0001

If it’s hung

kill -9 <PID>

That usually frees the port immediately.


Step 5: “I killed it, but the port is still busy”

This is less common, but it happens.

1) You’re checking the wrong port name

Double-check the exact device path.

2) There’s a second process holding it

Run the check again:

lsof /dev/cu.usbserial-0001

3) The USB chain is acting up (hub/dock/adapter)

Fast hardware fix:

  • unplug/replug the adapter
  • try a different USB port
  • if you’re on a dock, try plugging directly into the Mac

After replugging, the device name can change, so re-list ports:

ls -1 /dev/cu.*

How to avoid this next time

1) If you use screen a lot, glance at sessions before connecting

screen -ls

It takes one second and often reveals the problem instantly.

2) Always exit screen cleanly

Closing the Terminal window isn’t the same as closing the port. Use Ctrl+AKy.

3) If you write scripts, close the port on errors

If you’re using Python/pyserial, make sure you close the port reliably (e.g., try/finally or a context manager). Otherwise a process can stay alive and keep the device locked.


Mini command cheat sheet

Who’s holding the port:

lsof /dev/cu.usbserial-0001

List screen sessions:

screen -ls

Take over an attached screen session:

screen -d -r <ID>

Stop a process:

kill <PID>

Force-stop:

kill -9 <PID>