Skip to content

Troubleshooting

Serial communication fails in predictable ways. This guide covers the most common errors, how to diagnose them, and the recovery patterns that work.

All troubleshooting operations described here are performed through your MCP client. Ask the assistant to check port status, re-enumerate devices, or test connections — it calls the appropriate mcserial tools on your behalf.


You attempted to open a port that mcserial has already opened. This happens when:

  • A previous session opened the port but the close was never called
  • You called open_serial_port twice on the same device
  • Multiple agents or conversations are trying to use the same hardware

Recovery:

// Close the existing connection first
// close_serial_port(port="/dev/ttyUSB0")
{
"success": true,
"port": "/dev/ttyUSB0"
}
// Now open fresh
// open_serial_port(port="/dev/ttyUSB0", baudrate=115200)

If you are not sure which ports are open, check the connection status:

// get_connection_status()
{
"open_connections": [
{
"port": "/dev/ttyUSB0",
"mode": "rs232",
"baudrate": 115200
}
]
}

You tried to read, write, or configure a port that is not currently open in mcserial.

Common causes:

  • The port was never opened
  • The port was closed by a previous operation
  • The device was physically disconnected, causing an automatic close

Recovery:

Open the port before performing operations:

// open_serial_port(port="/dev/ttyUSB0", baudrate=115200)

If the device was disconnected, re-enumerate to confirm it is back:

// list_serial_ports()
{
"ports": [
{
"device": "/dev/ttyUSB0",
"description": "USB-Serial Controller",
"hwid": "USB VID:PID=0403:6001"
}
]
}

mcserial limits concurrent open ports to prevent resource exhaustion (default: 10). You have hit that limit.

Recovery:

Close ports you are no longer using:

// get_connection_status()
// Identify unused ports, then:
// close_serial_port(port="/dev/ttyUSB1")

If you genuinely need more concurrent connections, increase the limit via environment variable before starting mcserial:

Terminal window
export MCSERIAL_MAX_CONNECTIONS=20

See the Environment Variables reference for details.


The user running mcserial does not have access to the serial device. On Linux, serial ports are typically owned by the dialout or uucp group.

Terminal window
# Add your user to the dialout group
sudo usermod -a -G dialout $USER
# Log out and back in for the change to take effect
# Or use newgrp for the current session:
newgrp dialout

If devices keep losing permissions after reconnection, create a udev rule:

/etc/udev/rules.d/99-serial.rules
SUBSYSTEM=="tty", GROUP="dialout", MODE="0660"
# Reload rules
sudo udevadm control --reload-rules
sudo udevadm trigger

For specific devices (e.g., all FTDI adapters):

/etc/udev/rules.d/99-ftdi.rules
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", GROUP="dialout", MODE="0660"

When a USB serial device is physically unplugged, subsequent operations return errors. The exact error varies by operation:

  • Reads may return empty data or timeout
  • Writes may throw serial.serialutil.SerialException
  • Status queries may report the port as closed

Detection pattern:

// Try a simple read with short timeout
// read_serial(port="/dev/ttyUSB0", size=1, timeout=0.5)
// If the device is gone, you'll see:
{
"error": "Port /dev/ttyUSB0 is not open",
"success": false
}

After plugging a device back in, it may appear on a different port (e.g., /dev/ttyUSB1 instead of /dev/ttyUSB0). Always re-enumerate:

// list_serial_ports(grep="FTDI")
{
"ports": [
{
"device": "/dev/ttyUSB1",
"description": "FT232R USB UART",
"hwid": "USB VID:PID=0403:6001 SER=A50285BI"
}
]
}
  1. Detect the disconnection

    Monitor for errors or empty reads that indicate the device is gone.

  2. Close the stale connection

    // close_serial_port(port="/dev/ttyUSB0")

    This cleans up mcserial’s internal state even if the device is gone.

  3. Wait for reconnection

    Poll list_serial_ports until the device reappears.

  4. Open the new port

    // list_serial_ports(grep="FTDI")
    // open_serial_port(port="/dev/ttyUSB1", baudrate=115200)

Differentiating “no data” from “wrong baud rate”

Section titled “Differentiating “no data” from “wrong baud rate””

When read_serial returns empty, the cause could be:

  1. Correct baud, device not sending — the device is connected but silent
  2. Wrong baud rate — data is arriving but garbled or misframed
  3. Hardware issue — cable problem, device powered off, TX/RX swapped

Diagnosis steps:

// 1. Check if the port is actually open and configured
// get_connection_status()
// 2. Try auto-baud detection on an open port
// detect_baud_rate(port="/dev/ttyUSB0", probe="U", timeout_per_rate=0.5)
{
"detected_baudrate": 115200,
"confidence": 0.85,
"results": [
{"baudrate": 115200, "score": 0.85},
{"baudrate": 9600, "score": 0.12}
]
}

If auto-detection succeeds with a different baud rate, reconfigure:

// configure_serial(port="/dev/ttyUSB0", baudrate=115200)

Some devices take time to respond:

  • GPS receivers during cold start: 30-60 seconds
  • Industrial sensors with long sample cycles
  • Devices that process commands before responding

Adjust the read timeout:

// configure_serial(port="/dev/ttyUSB0", timeout=10.0)

Or specify per-read:

// read_serial(port="/dev/ttyUSB0", timeout=30.0)

See the Timeout Tuning guide for detailed guidance.


If RS-485 transactions return nothing, check:

  1. Termination — RS-485 buses need 120Ω termination resistors at each end
  2. Bias resistors — long buses or noisy environments may need bias
  3. A/B polarity — some adapters swap A and B (try swapping the wires)
  4. DE/RE timing — the driver enable timing may be wrong

Check RS-485 configuration:

// The port should be in RS-485 mode
// get_connection_status()
{
"open_connections": [
{
"port": "/dev/ttyUSB0",
"mode": "rs485",
"rs485_config": {
"enabled": true,
"rts_level_for_tx": true,
"delay_before_tx": 0,
"delay_before_rx": 0.005
}
}
]
}

If multiple devices respond simultaneously or responses are corrupted:

  • Address collision — two devices have the same address
  • Timing overlap — insufficient turnaround delay between TX and RX
  • Echo enabled — loopback mode is creating interference

Increase turnaround delay:

// set_rs485_mode(port="/dev/ttyUSB0", delay_before_rx=0.01)

Use rs485_scan_addresses to discover which devices respond:

// rs485_scan_addresses(
// port="/dev/ttyUSB0",
// start_address=1,
// end_address=32,
// response_timeout=0.1
// )
{
"success": true,
"responding_addresses": [1, 5, 12],
"total_scanned": 32,
"scan_time_seconds": 3.2
}

Auto-baud detection relies on seeing data to analyze. It fails when:

  • The device is not sending anything (waiting for a command)
  • The device requires a specific wake-up sequence
  • All baud rates produced only garbled data (hardware issue)

Try with a probe string:

// Some devices echo input or respond to specific commands
// open_serial_port(
// port="/dev/ttyUSB0",
// autobaud_probe="ATI\r\n",
// autobaud_timeout=0.5
// )

The probe is sent at each candidate baud rate. If the device echoes or responds, the detection has data to analyze.

Common probe strings:

Device TypeProbeNotes
Modem/AT commandATI\r\nReturns device info
Echo-enabledUUUUU0x55 is a sync pattern
GPS (NMEA)(none)Usually sends continuously
Modbus RTU(protocol-specific)Send a read holding register command

If detection consistently picks the wrong rate, the heuristics may be confused by the data pattern. Fall back to explicit baud rate specification:

// open_serial_port(port="/dev/ttyUSB0", baudrate=115200)

”Receiver not ready” or immediate timeout

Section titled “”Receiver not ready” or immediate timeout”

The remote device must be in receive mode before you call file_transfer_send. For XMODEM and YMODEM, start the receiver first.

Typical workflow:

// 1. Send the receive command to the device
// write_serial(port="/dev/ttyUSB0", data="rx firmware.bin\r\n")
// 2. Wait for the receiver to start (look for NAK or 'C' for CRC mode)
// read_serial(port="/dev/ttyUSB0", timeout=5.0)
// 3. Now send the file
// file_transfer_send(port="/dev/ttyUSB0", file_path="/tmp/firmware.bin", protocol="xmodem")

ZMODEM supports resume. If a transfer was interrupted:

// Just try sending again — ZMODEM negotiates resume automatically
// file_transfer_send(port="/dev/ttyUSB0", file_path="/tmp/large_file.bin", protocol="zmodem")

The receiver should offer to resume from where it left off, skipping already-received data.

All protocols have error detection and will request retransmission of corrupted blocks. However, excessive errors indicate:

  • Electrical noise — use shielded cables, add ferrite chokes
  • Baud rate too high — reduce baud rate for long cables
  • Flow control issues — enable RTS/CTS for high-speed transfers

See File Transfers for protocol-specific details.


Error MessageLikely CauseFirst Action
”Port is already open”Double openclose_serial_port, then reopen
”Port is not open”Never opened or disconnectedopen_serial_port
”Permission denied”Missing dialout groupsudo usermod -a -G dialout $USER
”Maximum connections reached”Too many open portsClose unused ports
”Device or resource busy”Another process has the portCheck lsof /dev/ttyUSB0
Empty reads, no dataWrong baud or device silentdetect_baud_rate or probe
Garbled textWrong baud rateconfigure_serial(baudrate=...)
”Unsupported URL scheme”Typo in URLCheck scheme spelling
”cp2110:// requires hidapi”Missing extrapip install mcserial[cp2110]