zeroclaw-labs/zeroclaw · error
Serial path not allowed: {}. Allowed: {}
Error message
Serial path not allowed: {}. Allowed: {} What it means
SerialPeripheral::connect enforces a hardcoded device-path allowlist: /dev/ttyACM*, /dev/ttyUSB*, /dev/tty.usbmodem*, /dev/cu.usbmodem*, /dev/tty.usbserial*, /dev/cu.usbserial*, COM* (plus /tmp/zc-sim-* when built with the dev-sim feature). Any configured path that does not start with one of these prefixes is rejected before the port is opened; the message lists the accepted prefixes.
Source
Thrown at crates/zeroclaw-hardware/src/peripherals/serial.rs:148
}
impl SerialPeripheral {
/// Create and connect to a serial peripheral.
#[allow(clippy::unused_async)]
pub async fn connect(config: &PeripheralBoardConfig) -> anyhow::Result<Self> {
let path = config.path.as_deref().ok_or_else(|| {
::zeroclaw_log::record!(
WARN,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
.with_outcome(::zeroclaw_log::EventOutcome::Failure)
.with_attrs(::serde_json::json!({"board": config.board})),
"serial peripheral connect refused: config missing 'path'"
);
anyhow::Error::msg("Serial peripheral requires path")
})?;
if !is_serial_path_allowed(path) {
anyhow::bail!(
"Serial path not allowed: {}. Allowed: {}",
path,
serial_path_allowlist_hint()
);
}
let builder = tokio_serial::new(path, serial_open_baud(path, config.baud));
#[cfg(unix)]
let builder = if should_open_serial_nonexclusive(path) {
builder.exclusive(false)
} else {
builder
};
let port = builder.open_native_async().map_err(|e| {
::zeroclaw_log::record!(
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
.with_outcome(::zeroclaw_log::EventOutcome::Failure)View on GitHub (pinned to 88bb9c8533)
Solutions
- Point path at the device's real node under an allowed prefix: /dev/ttyACM* or /dev/ttyUSB* on Linux, /dev/cu.usbmodem* on macOS, COM<n> on Windows
- Do not use /dev/serial/by-id or /dev/ttyS* paths — the allowlist matches prefixes literally; use the underlying ttyACM/ttyUSB node instead
- Fix format details: uppercase COM with a number; the leading /dev/ is required on Unix
Example fix
# before [[peripherals.boards]] board = "nucleo-f401re" transport = "serial" path = "/dev/serial/by-id/usb-STMicroelectronics-CDC-1234" # after [[peripherals.boards]] board = "nucleo-f401re" transport = "serial" path = "/dev/ttyACM0"
Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED: [&str; 7] = [
"/dev/ttyACM", "/dev/ttyUSB", "/dev/tty.usbmodem", "/dev/cu.usbmodem",
"/dev/tty.usbserial", "/dev/cu.usbserial", "COM",
];
fn serial_path_allowed(path: &str) -> bool {
ALLOWED.iter().any(|p| path.starts_with(p))
}
if !serial_path_allowed(&config.path.clone().unwrap_or_default()) {
anyhow::bail!("serial path rejected by allowlist; use /dev/ttyACM* or COM*");
}
SerialPeripheral::connect(&config).await?; Type guard
fn is_allowed_serial_path(path: &str) -> bool {
["/dev/ttyACM", "/dev/ttyUSB", "/dev/tty.usbmodem", "/dev/cu.usbmodem",
"/dev/tty.usbserial", "/dev/cu.usbserial", "COM"]
.iter().any(|p| path.starts_with(p))
} Prevention
- Always configure the real ttyACM/ttyUSB/usbmodem/COM node, not symlinks like /dev/serial/by-id
- Validate the path against the prefix list in config-loading code, not at connect time
- Remember the allowlist is hardcoded — /dev/ttyS* devices cannot be used
When it happens
Trigger: Configuring a [[peripherals.boards]] entry with path = "/dev/ttyS0" (legacy UART), a stable symlink like /dev/serial/by-id/usb-... (fails: not an allowed prefix), a relative path like ttyACM0, or a typo such as /dev/ttyACN0.
Common situations: Boards exposing legacy ttyS* nodes; users preferring /dev/serial/by-id paths for stability; lowercase com3 on Windows; configs ported from tools that accept any device path.
Related errors
- Destination {} is not in allowed list
- Edge TTS binary_path must be a bare command name without pat
- Edge TTS binary_path must be one of {:?}, got: {raw_path}
- webhook channel requires a `secret` configured for request a
- Refusing to transmit sensitive data over non-HTTPS URL: URL
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/350f766669b52258.
Report an issue: GitHub.