ruvnet/RuView · error · ValueError

Invalid MAC address format

Error message

Invalid MAC address format

What it means

SQLAlchemy @validates hook on Device.mac_address in the archive/v1 registry models. It accepts only a 17-character colon-separated MAC (six 2-character hex groups, 'XX:XX:XX:XX:XX:XX'), lowercases it, and raises ValueError for every other shape. Because it is a field validator, the error fires at attribute assignment time (Device(mac_address=...) or device.mac_address = ...), before flush or commit.

Source

Thrown at archive/v1/src/database/models.py:110

    csi_data = relationship("CSIData", back_populates="device", cascade="all, delete-orphan")
    
    # Constraints and indexes
    __table_args__ = (
        Index("idx_device_mac_address", "mac_address"),
        Index("idx_device_status", "status"),
        Index("idx_device_type", "device_type"),
        CheckConstraint("status IN ('active', 'inactive', 'maintenance', 'error')", name="check_device_status"),
    )
    
    @validates('mac_address')
    def validate_mac_address(self, key, address):
        """Validate MAC address format."""
        if address and len(address) == 17:
            # Basic MAC address format validation
            parts = address.split(':')
            if len(parts) == 6 and all(len(part) == 2 for part in parts):
                return address.lower()
        raise ValueError("Invalid MAC address format")
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "id": str(self.id),
            "name": self.name,
            "device_type": self.device_type,
            "mac_address": self.mac_address,
            "ip_address": self.ip_address,
            "status": self.status,
            "firmware_version": self.firmware_version,
            "hardware_version": self.hardware_version,
            "location_name": self.location_name,
            "room_id": self.room_id,
            "coordinates": {
                "x": self.coordinates_x,
                "y": self.coordinates_y,
                "z": self.coordinates_z,

View on GitHub (pinned to 4685618388)

Solutions

  1. Normalize the MAC before assignment: extract hex digits, validate 12 of them, rejoin with ':' and lowercase (see exampleFix)
  2. Pre-validate with a regex during bulk import and skip/log bad rows instead of letting the ORM raise mid-transaction
  3. If empty MACs must be representable, change the validator to accept falsy input (return None) and make the column nullable — model change plus migration

Example fix

# before
device = Device(name='node1', mac_address='00-1A-2B-3C-4D-5E')  # ValueError: Invalid MAC address format

# after
import re

def normalize_mac(raw: str) -> str:
    digits = re.sub(r'[^0-9A-Fa-f]', '', raw or '')
    if len(digits) != 12:
        raise ValueError(f'Invalid MAC address format: {raw!r}')
    return ':'.join(digits[i:i+2] for i in range(0, 12, 2)).lower()

device = Device(name='node1', mac_address=normalize_mac('00-1A-2B-3C-4D-5E'))  # ok
Defensive patterns

Strategy: validation

Validate before calling

import re

MAC_RE = re.compile(r'([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}')

def normalize_mac(raw):
    digits = re.sub(r'[^0-9A-Fa-f]', '', raw or '')
    if len(digits) != 12:
        raise ValueError(f'Invalid MAC address format: {raw!r}')
    return ':'.join(digits[i:i+2] for i in range(0, 12, 2)).lower()

# before creating the row:
mac = normalize_mac(row['mac_address'])
device = Device(name=row['name'], mac_address=mac)

Type guard

def is_valid_mac(value) -> bool:
    return bool(value) and len(value) == 17 and MAC_RE.fullmatch(value) is not None

Try / catch

try:
    device = Device(name=name, mac_address=normalize_mac(raw_mac))
except ValueError as e:
    logger.warning('skipping row with bad MAC %r: %s', raw_mac, e)
    continue

Prevention

When it happens

Trigger: Constructing Device(...) or assigning .mac_address with '00-1A-2B-3C-4D-5E' (dashes), '001a2b3c4d5e' (no separators), 'AA:BB:CC:DD:EE' (5 groups / 14 chars), or '' / None. The constructor path also fires the validator, so bulk inserts via Device(**row) raise on the first malformed row.

Common situations: Importing device inventories produced by Windows tools (dash format) or nmap (colon-free hex); test fixtures with placeholder MACs like 'AA:BB:CC:DD:EE'; assuming an empty MAC means 'unassigned' (rejected); uppercase input passes but is silently lowercased.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/30a65f92509d2350. Report an issue: GitHub.