Error shape
| Property | Type | Notes |
|---|---|---|
code | SerialPilotErrorCode | Stable enum-like string (e.g. 'PORT_NOT_FOUND'). |
message | string | Human-readable description. |
advice | string | What to do about it. |
path | string? | The port path involved, when known. |
baudRate | number? | The baud rate, when known. |
cause | Error? | The underlying OS error, when one exists. |
Error catalogue
| Code | Class | Cause | Recovery |
|---|---|---|---|
PORT_NOT_FOUND | PortNotFoundError | Path wrong or device unplugged. | Re-enumerate; use findPorts(). |
PERMISSION_DENIED | PermissionDeniedError | OS denied access to the device. | Linux: add user to dialout. macOS: grant in System Settings → Privacy. |
PORT_BUSY | PortBusyError | Another process holds the port. | Close the other app (Arduino IDE, screen, PuTTY). |
DISCONNECTED | DisconnectedError | Device unplugged mid-session. | Use @serialpilot/reconnect for resilient sessions. |
OPEN_FAILED | OpenFailedError | Generic open failure (often follows a more specific error). | Inspect err.cause for the OS reason. |
WRITE_FAILED | WriteFailedError | Wrote to a port that's gone. | Check port.isOpen before writing in flaky environments. |
READ_FAILED | ReadFailedError | Read from a port that's gone. | Same as above. |
CANCELLED | CancelledError | An in-flight operation was aborted. | Expected when calling close() with pending I/O — usually safe to ignore. |
INVALID_ARGUMENT | InvalidArgumentError | Bad constructor args. | Validate path and baudRate. |
TIMEOUT | TimeoutError | Operation exceeded its budget (mostly from command-queue). | Raise the timeout, retry, or check device responsiveness. |
Catching
import { SerialPilot, SerialPilotError, PortNotFoundError, PermissionDeniedError, PortBusyError, DisconnectedError, } from 'serialpilot'
try { const port = new SerialPilot({ path: ‘/dev/ttyUSB0’, baudRate: 9600 }) } catch (err) { if (err instanceof PortNotFoundError) { console.error(err.advice) } else if (err instanceof PermissionDeniedError) { console.error(err.advice) } else if (err instanceof SerialPilotError) { console.error(${err.code}: ${err.message}) console.error(Advice: ${err.advice}) } }
Error events
Asynchronous failures arrive on the port's error event. Always attach a listener — an unhandled stream error crashes the Node process.
port.on('error', err => {
if (err instanceof DisconnectedError) {
// trigger reconnect
} else {
log.error({ code: err.code, advice: err.advice }, err.message)
}
})
Reliability strategies
- Auto-reconnect. Wrap your port with
SerialPilotReconnectin production. It handles cable yanks, USB-path reshuffles, and exponential backoff. - Open-by-device. Use
SerialPilot.openByDevice({ vendorId, productId })instead of pinning to a path — paths shift between hosts and OSes. - Honour backpressure. If
port.write()returnsfalse, wait for'drain'. Continually writing past the high-water mark causes ballooning memory and ultimatelyWRITE_FAILED. - Wrap promises. The constructor and
close()/open()are callback-based; promisify them at the boundary so errors surface as rejections. - Test with the mock. The same error classes work against
MockBinding. UsedisconnectAfterto assert your reconnect path runs.
Gotcha
CancelledError is fired on every pending operation when you close() a port. That's normal — your code should ignore CancelledError in any catch block that could see one mid-shutdown.