{"record":{"id":"30a65f92509d2350","repo":"ruvnet/RuView","slug":"invalid-mac-address-format","errorCode":null,"errorMessage":"Invalid MAC address format","messagePattern":"Invalid MAC address format","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"archive/v1/src/database/models.py","lineNumber":110,"sourceCode":"    csi_data = relationship(\"CSIData\", back_populates=\"device\", cascade=\"all, delete-orphan\")\n    \n    # Constraints and indexes\n    __table_args__ = (\n        Index(\"idx_device_mac_address\", \"mac_address\"),\n        Index(\"idx_device_status\", \"status\"),\n        Index(\"idx_device_type\", \"device_type\"),\n        CheckConstraint(\"status IN ('active', 'inactive', 'maintenance', 'error')\", name=\"check_device_status\"),\n    )\n    \n    @validates('mac_address')\n    def validate_mac_address(self, key, address):\n        \"\"\"Validate MAC address format.\"\"\"\n        if address and len(address) == 17:\n            # Basic MAC address format validation\n            parts = address.split(':')\n            if len(parts) == 6 and all(len(part) == 2 for part in parts):\n                return address.lower()\n        raise ValueError(\"Invalid MAC address format\")\n    \n    def to_dict(self) -> Dict[str, Any]:\n        \"\"\"Convert to dictionary.\"\"\"\n        return {\n            \"id\": str(self.id),\n            \"name\": self.name,\n            \"device_type\": self.device_type,\n            \"mac_address\": self.mac_address,\n            \"ip_address\": self.ip_address,\n            \"status\": self.status,\n            \"firmware_version\": self.firmware_version,\n            \"hardware_version\": self.hardware_version,\n            \"location_name\": self.location_name,\n            \"room_id\": self.room_id,\n            \"coordinates\": {\n                \"x\": self.coordinates_x,\n                \"y\": self.coordinates_y,\n                \"z\": self.coordinates_z,","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/ruvnet/RuView/blob/4685618388a5e49fad5b3005806f3bdd6a7c25c3/archive/v1/src/database/models.py#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\ndevice = Device(name='node1', mac_address='00-1A-2B-3C-4D-5E')  # ValueError: Invalid MAC address format\n\n# after\nimport re\n\ndef normalize_mac(raw: str) -> str:\n    digits = re.sub(r'[^0-9A-Fa-f]', '', raw or '')\n    if len(digits) != 12:\n        raise ValueError(f'Invalid MAC address format: {raw!r}')\n    return ':'.join(digits[i:i+2] for i in range(0, 12, 2)).lower()\n\ndevice = Device(name='node1', mac_address=normalize_mac('00-1A-2B-3C-4D-5E'))  # ok","handlingStrategy":"validation","validationCode":"import re\n\nMAC_RE = re.compile(r'([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}')\n\ndef normalize_mac(raw):\n    digits = re.sub(r'[^0-9A-Fa-f]', '', raw or '')\n    if len(digits) != 12:\n        raise ValueError(f'Invalid MAC address format: {raw!r}')\n    return ':'.join(digits[i:i+2] for i in range(0, 12, 2)).lower()\n\n# before creating the row:\nmac = normalize_mac(row['mac_address'])\ndevice = Device(name=row['name'], mac_address=mac)","typeGuard":"def is_valid_mac(value) -> bool:\n    return bool(value) and len(value) == 17 and MAC_RE.fullmatch(value) is not None","tryCatchPattern":"try:\n    device = Device(name=name, mac_address=normalize_mac(raw_mac))\nexcept ValueError as e:\n    logger.warning('skipping row with bad MAC %r: %s', raw_mac, e)\n    continue","preventionTips":["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"],"tags":["database","sqlalchemy","validation","mac-address","orm"],"backgroundTag":null,"analyzedSha":"4685618388a5e49fad5b3005806f3bdd6a7c25c3","analyzedAt":"2026-08-16T06:09:40.886Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}