Error Codes

When a command fails, the device responds with a NAK packet (0x83) containing a 1-byte error code. The NAK echoes the seq of the failed command.

Error Table

CodeNameConstantDescription
0x01UNKNOWN_TYPECONDUYT_ERR_UNKNOWN_TYPEUnrecognized command type byte
0x02CRC_MISMATCHCONDUYT_ERR_CRC_MISMATCHPacket CRC8 validation failed
0x03PAYLOAD_TOO_LARGECONDUYT_ERR_PAYLOAD_TOO_LARGEPayload exceeds device buffer
0x04INVALID_PINCONDUYT_ERR_INVALID_PINPin number out of range
0x05PIN_MODE_UNSUPPORTEDCONDUYT_ERR_PIN_MODE_UNSUPPORTEDPin does not support requested mode
0x06I2C_NOT_AVAILABLECONDUYT_ERR_I2C_NOT_AVAILABLEI2C bus not initialized or unavailable
0x07I2C_NACKCONDUYT_ERR_I2C_NACKI2C device did not acknowledge
0x08MODULE_NOT_LOADEDCONDUYT_ERR_MODULE_NOT_LOADEDModule ID not registered
0x09UNKNOWN_MODULE_CMDCONDUYT_ERR_UNKNOWN_MODULE_CMDModule does not handle this command byte
0x0AMODULE_BUSYCONDUYT_ERR_MODULE_BUSYModule is mid-operation (e.g., stepper moving)
0x0BSUB_LIMIT_REACHEDCONDUYT_ERR_SUB_LIMIT_REACHEDMax subscriptions reached
0x0COUT_OF_MEMORYCONDUYT_ERR_OUT_OF_MEMORYAllocation failed
0x0DUNKNOWN_DATASTREAMCONDUYT_ERR_UNKNOWN_DATASTREAMDatastream index not found
0x0EDATASTREAM_READONLYCONDUYT_ERR_DATASTREAM_READONLYWrite attempted on read-only datastream
0x0FOTA_INVALIDCONDUYT_ERR_OTA_INVALIDOTA not supported or sequence error
0x10VERSION_MISMATCHCONDUYT_ERR_VERSION_MISMATCHProtocol version incompatible

Common Scenarios

INVALID_PIN (0x04)

You passed a pin number higher than the device supports. Check device.capabilities.pins.length before addressing a pin.

const pinCount = device.capabilities.pins.length
if (pinIndex >= pinCount) {
  throw new Error(`Pin ${pinIndex} out of range, device has ${pinCount} pins`)
}
await device.pin(pinIndex).write(1)

PIN_MODE_UNSUPPORTED (0x05)

The pin does not support the requested mode. Check the pin's capability bitmask in device.capabilities.pins[n].capabilities.

const pin = device.capabilities.pins[9]
const supportsPWM = pin.capabilities & 0x04
if (!supportsPWM) {
  throw new Error(`Pin 9 does not support PWM`)
}
await device.pin(9).mode('pwm')

MODULE_NOT_LOADED (0x08)

You sent a MOD_CMD for a module ID that the firmware did not register. Verify the module is registered with device.addModule() and that the module_id matches the registration order.

// Firmware side: modules are assigned IDs in registration order
device.addModule(servoModule);   // module_id = 0
device.addModule(stepperModule); // module_id = 1

If the host sends a command to module_id 2 and only two modules are registered, the device returns MODULE_NOT_LOADED.

PAYLOAD_TOO_LARGE (0x03)

Your payload exceeds the device's buffer. Check device.capabilities.maxPayload for the limit. On ATmega328 boards this is 128 bytes.

const max = device.capabilities.maxPayload
if (data.length > max) {
  throw new Error(`Payload ${data.length} bytes exceeds limit of ${max}`)
}

Host Handling

JavaScript

import { ConduytNAKError } from 'conduyt-js'

try {
  await device.pin(99).write(1)
} catch (e) {
  if (e instanceof ConduytNAKError) {
    console.log(e.errorName) // "INVALID_PIN"
    console.log(e.code)      // 0x04
  }
}

The errorName property maps the numeric code to its string name. The code property holds the raw byte value.

Python

from conduyt import ConduytNAKError

try:
    await device.pin(99).write(1)
except ConduytNAKError as e:
    print(e.error_name)  # "INVALID_PIN"
    print(e.code)        # 0x04

Python uses error_name (snake_case), not errorName.

Retry Strategy

NAK errors are deterministic. Retrying the same command produces the same error. Fix the root cause (wrong pin number, unsupported mode, missing module) before retrying. Timeout errors, by contrast, may be transient and worth retrying once.