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
- Normalize the MAC before assignment: extract hex digits, validate 12 of them, rejoin with ':' and lowercase (see exampleFix)
- Pre-validate with a regex during bulk import and skip/log bad rows instead of letting the ORM raise mid-transaction
- 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
- Normalize every external MAC string (dash, dot, or bare hex forms) through one normalize_mac helper before ORM assignment
- Keep a unit test asserting the validator accepts 17-char colon hex and rejects dashes/empty/partial MACs
- Never rely on flush/commit-time errors for MAC validation — @validates fires at assignment, so validate at the ingestion boundary
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
- Database port must be between 1 and 65535
- Database pool size must be at least 1
- brain corpus exceeds 1000 records
- command must be a non-empty string
- args must be an array of strings
AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16).
Data as JSON: /api/errors/30a65f92509d2350.
Report an issue: GitHub.