SerialPilot

/03 — Recipes

Error handling

SerialPilot's errors come with a stable code, a human message, and an actionable advice string. Treat them as data, not as flammable strings — your operations team will thank you.

Error anatomy

Every SerialPilotError instance has:

  • code — a SerialPilotErrorCode enum value (e.g. 'PORT_NOT_FOUND').
  • message — a human-readable description.
  • advice — what to actually do about it.
  • path / baudRate — context, when known.
  • cause — the underlying OS error, when one exists.

Catalogue

CodeClassCauseAdvice
PORT_NOT_FOUNDPortNotFoundErrorDevice unplugged or path wrongCheck the cable; use findPorts()
PERMISSION_DENIEDPermissionDeniedErrorInsufficient OS permissionsLinux: add user to dialout
PORT_BUSYPortBusyErrorAnother app is holding the portClose Arduino IDE, screen, PuTTY, etc.
DISCONNECTEDDisconnectedErrorDevice unplugged mid-sessionUse @serialpilot/reconnect
OPEN_FAILEDOpenFailedErrorGeneric open failureInspect err.cause for the OS reason
WRITE_FAILEDWriteFailedErrorWrote to a port that's goneCheck port.isOpen; honour backpressure
READ_FAILEDReadFailedErrorRead from a port that's goneSame as above
CANCELLEDCancelledErrorOperation abortedNormal during close() — usually safe to ignore
INVALID_ARGUMENTInvalidArgumentErrorBad constructor argsValidate path and baudRate
TIMEOUTTimeoutErrorOperation took too longBump the timeout, retry, or check the device

Catching at construction time

import {
SerialPilot,
SerialPilotError,
PortNotFoundError,
PermissionDeniedError,
} 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}) } }

Catching at runtime

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 logic, alert your fleet
} else if (err instanceof CancelledError) {
// pending operation cancelled by close() — usually fine
} else {
log.error({ code: err.code, advice: err.advice }, err.message)
}
})

Structured logging

Treat the error like a record:

logger.error({
code:     err.code,
path:     err.path,
baudRate: err.baudRate,
advice:   err.advice,
cause:    err.cause?.message,
}, err.message)

Now your dashboards can group by code rather than free-text matching, and alerts can target specific failure modes.

Don't swallow CancelledError …unless you mean to. It's expected during close(), but seeing it elsewhere usually means a destroy()/abort() happened mid-flight — which may or may not be a bug in your code.

Edit this page on GitHub