API: 500 Internal Server Error when creating multiple modules of the same type on the same device #11128

Closed
opened 2025-12-29 21:40:42 +01:00 by adam · 2 comments
Owner

Originally created by @Kani999 on GitHub (May 6, 2025).

Deployment Type

Self-hosted

NetBox Version

v4.2.8

Python Version

3.12

Steps to Reproduce

I'm encountering a 500 Internal Server Error when trying to create multiple modules of the same type on the same device using the NetBox API.

When creating a module, NetBox appears to automatically create associated interfaces. If two modules of the same type are added to the same device, NetBox tries to create interfaces with identical names, which violates the unique constraint dcim_interface_unique_device_name.

Steps to Reproduce

  1. Have a device with at least two module bays
  2. Have a module type that includes interface templates
  3. Create a module in the first module bay
  4. Try to create another module of the same type in the second module bay

pynetbox script to reproduce the error:

import pynetbox
import random
import string
import time

# Replace with your NetBox URL and token
NETBOX_URL = "https://your-netbox-instance.example.com"
API_TOKEN = "your-api-token"

# Initialize PyNetbox API
nb = pynetbox.api(
    NETBOX_URL,
    token=API_TOKEN
)

def random_string(length=8):
    """Generate a random string of fixed length."""
    return ''.join(random.choices(string.ascii_letters + string.digits, k=length))

def setup_and_reproduce_issue():
    """
    Set up a complete test environment and reproduce the interface duplication issue.
    This function will:
    1. Create a manufacturer
    2. Create a device type with module bays
    3. Create a site and device role
    4. Create a device
    5. Create a module type with interface templates
    6. Create modules that will trigger the error
    """
    # Generate a random prefix for unique naming for every run
    prefix = f"test-{random_string(6)}"
    
    # Step 1: Create a manufacturer
    print("Creating manufacturer...")
    manufacturer = nb.dcim.manufacturers.create(
        name=f"{prefix}-manufacturer",
        slug=f"{prefix}-manufacturer"
    )
    print(f"Created manufacturer: {manufacturer.name}")
    
    # Step 2: Create a device type with module bays
    print("Creating device type...")
    device_type = nb.dcim.device_types.create(
        manufacturer=manufacturer.id,
        model=f"{prefix}-device-type",
        slug=f"{prefix}-device-type"
    )
    print(f"Created device type: {device_type.model}")
    
    # Create module bay templates
    print("Creating module bay templates...")
    module_bay_template1 = nb.dcim.module_bay_templates.create(
        device_type=device_type.id,
        name="Module Bay 1"
    )
    module_bay_template2 = nb.dcim.module_bay_templates.create(
        device_type=device_type.id,
        name="Module Bay 2"
    )
    print(f"Created module bay templates")
    
    # Step 3: Create a site
    print("Creating site...")
    site = nb.dcim.sites.create(
        name=f"{prefix}-site",
        slug=f"{prefix}-site",
        status="active"
    )
    print(f"Created site: {site.name}")
    
    # Create a device role
    print("Creating device role...")
    device_role = nb.dcim.device_roles.create(
        name=f"{prefix}-role",
        slug=f"{prefix}-role",
        color="ff0000"
    )
    print(f"Created device role: {device_role.name}")
    
    # Step 4: Create a device
    print("Creating device...")
    device = nb.dcim.devices.create(
        name=f"{prefix}-device",
        device_type=device_type.id,
        site=site.id,
        role=device_role.id,  # Added the required role field
        status="active"
    )
    print(f"Created device: {device.name}")
    
    # Wait for module bays to be created
    print("Waiting for module bays to be created...")
    time.sleep(2)
    
    # Get the module bays
    module_bays = list(nb.dcim.module_bays.filter(device_id=device.id))
    if len(module_bays) < 2:
        print(f"Error: Expected 2 module bays, but found {len(module_bays)}")
        return
    
    module_bay1 = module_bays[0]
    module_bay2 = module_bays[1]
    print(f"Found module bays: {module_bay1.name}, {module_bay2.name}")
    
    # Step 5: Create a module type with interface templates
    print("Creating module type...")
    module_type = nb.dcim.module_types.create(
        manufacturer=manufacturer.id,
        model=f"{prefix}-module-type",
        part_number=random_string(8)
    )
    print(f"Created module type: {module_type.model}")
    
    # Create an interface template for the module type
    print("Creating interface template...")
    interface_template = nb.dcim.interface_templates.create(
        module_type=module_type.id,
        name="Network",
        type="100base-tx"
    )
    print(f"Created interface template: {interface_template.name}")
    
    # Step 6: Create first module (should succeed)
    print("\nCreating first module...")
    try:
        module1 = nb.dcim.modules.create(
            device=device.id,
            module_bay=module_bay1.id,
            module_type=module_type.id,
            name="Test Module"
        )
        print(f"Successfully created first module with ID: {module1.id}")
    except Exception as e:
        print(f"Error creating first module: {e}")
        return
    
    # Step 7: Create second module (should fail with 500 error)
    print("\nCreating second module (this should fail with a 500 error)...")
    try:
        module2 = nb.dcim.modules.create(
            device=device.id,
            module_bay=module_bay2.id,
            module_type=module_type.id,
            name="Test Module 2"  # Even with a different name, it will fail
        )
        print(f"Created second module with ID: {module2.id}")
    except Exception as e:
        print(f"ERROR ENCOUNTERED (expected): {e}")
        print("\nThis error demonstrates the issue with NetBox: When creating multiple modules")
        print("of the same type on the same device, NetBox tries to create interfaces with")
        print("identical names, violating the unique constraint for interface names per device.")
        raise e


if __name__ == "__main__":
    setup_and_reproduce_issue()

Expected Behavior

When creating multiple modules of the same type on the same device, NetBox should:

  • Either generate unique interface names for each module
  • Or append a suffix/numbering scheme to ensure uniqueness
  • Or provide a way to customize interface names during module creation
  • Or return a proper Error code instead of 500

Observed Behavior

Error Details

Script output:

Creating manufacturer...
Created manufacturer: test-C9NaMQ-manufacturer
Creating device type...
Created device type: test-C9NaMQ-device-type
Creating module bay templates...
Created module bay templates
Creating site...
Created site: test-C9NaMQ-site
Creating device role...
Created device role: test-C9NaMQ-role
Creating device...
Created device: test-C9NaMQ-device
Waiting for module bays to be created...
Found module bays: Module Bay 1, Module Bay 2
Creating module type...
Created module type: test-C9NaMQ-module-type
Creating interface template...
Created interface template: Network

Creating first module...
Successfully created first module with ID: 1962

Creating second module (this should fail with a 500 error)...
ERROR ENCOUNTERED (expected): The request failed with code 500 Internal Server Error but more specific details were not returned in json. Check the NetBox Logs or investigate this exception's error attribute.

This error demonstrates the issue with NetBox: When creating multiple modules
of the same type on the same device, NetBox tries to create interfaces with
identical names, violating the unique constraint for interface names per device.
Traceback (most recent call last):
  File "/Users/jankrupa/Work/pynetbox_api_scripts/my_scripts/power_port_duplicate_error_500.py", line 169, in <module>
    setup_and_reproduce_issue()
    ~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/Users/jankrupa/Work/pynetbox_api_scripts/my_scripts/power_port_duplicate_error_500.py", line 156, in setup_and_reproduce_issue
    raise e
  File "/Users/jankrupa/Work/pynetbox_api_scripts/my_scripts/power_port_duplicate_error_500.py", line 144, in setup_and_reproduce_issue
    module2 = nb.dcim.modules.create(
        device=device.id,
    ...<2 lines>...
        name="Test Module 2"  # Even with a different name, it will fail
    )
  File "/Users/jankrupa/Work/pynetbox_api_scripts/pynetbox/core/endpoint.py", line 417, in create
    ).post(args[0] if args else kwargs)
      ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jankrupa/Work/pynetbox_api_scripts/pynetbox/core/query.py", line 370, in post
    return self._make_call(verb="post", data=data)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/jankrupa/Work/pynetbox_api_scripts/pynetbox/core/query.py", line 260, in _make_call
    raise RequestError(req)
pynetbox.core.query.RequestError: The request failed with code 500 Internal Server Error but more specific details were not returned in json. Check the NetBox Logs or investigate this exception's error attribute.

The error from PostgreSQL:

ERROR: duplicate key value violates unique constraint "dcim_interface_unique_device_name"
DETAIL: Key (device_id, name)=(device_id, interface_name) already exists.

NetBox Log

netbox-1               | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/manufacturers/ HTTP/1.1" 201 388 "-" "python-requests/2.32.3"
netbox-1               | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/device-types/ HTTP/1.1" 201 1142 "-" "python-requests/2.32.3"
netbox-1               | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/module-bay-templates/ HTTP/1.1" 201 704 "-" "python-requests/2.32.3"
netbox-1               | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/module-bay-templates/ HTTP/1.1" 201 704 "-" "python-requests/2.32.3"
netbox-1               | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/sites/ HTTP/1.1" 201 649 "-" "python-requests/2.32.3"
netbox-1               | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/device-roles/ HTTP/1.1" 201 414 "-" "python-requests/2.32.3"
netbox-1               | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/devices/ HTTP/1.1" 201 2252 "-" "python-requests/2.32.3"
netbox-1               | XXXX - - [06/May/2025:12:57:09 +0000] "GET /api/dcim/module-bays/?device_id=500&limit=0 HTTP/1.1" 200 1145 "-" "python-requests/2.32.3"
netbox-1               | XXXX - - [06/May/2025:12:57:09 +0000] "POST /api/dcim/module-types/ HTTP/1.1" 201 650 "-" "python-requests/2.32.3"
netbox-1               | XXXX - - [06/May/2025:12:57:09 +0000] "POST /api/dcim/interface-templates/ HTTP/1.1" 201 806 "-" "python-requests/2.32.3"
netbox-1               | XXXX - - [06/May/2025:12:57:10 +0000] "POST /api/dcim/modules/ HTTP/1.1" 201 1162 "-" "python-requests/2.32.3"
postgres-1             | 2025-05-06 12:57:10.178 UTC [11072] ERROR:  duplicate key value violates unique constraint "dcim_interface_unique_device_name"
postgres-1             | 2025-05-06 12:57:10.178 UTC [11072] DETAIL:  Key (device_id, name)=(500, Network) already exists.
postgres-1             | 2025-05-06 12:57:10.178 UTC [11072] STATEMENT:  INSERT INTO "dcim_interface" ("created", "last_updated", "custom_field_data", "device_id", "name", "label", "description", "module_id", "cable_id", "cable_end", "mark_connected", "_path_id", "enabled", "mtu", "mode", "parent_id", "bridge_id", "untagged_vlan_id", "qinq_svlan_id", "vlan_translation_policy_id", "primary_mac_address_id", "_name", "lag_id", "type", "mgmt_only", "speed", "duplex", "wwn", "rf_role", "rf_channel", "rf_channel_frequency", "rf_channel_width", "tx_power", "poe_mode", "poe_type", "wireless_link_id", "vrf_id") VALUES ('2025-05-06 12:57:10.177735+00:00'::timestamptz, '2025-05-06 12:57:10.177749+00:00'::timestamptz, '{"ignore_importer": false, "review": false}'::jsonb, 500, 'Network', '', '', 1963, NULL, NULL, false, NULL, true, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '9999999999999999Network..................', NULL, '100base-tx', false, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) RETURNING "dcim_interface"."id"
netbox-1               | Internal Server Error: /api/dcim/modules/
netbox-1               | Traceback (most recent call last):
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 105, in _execute
netbox-1               |     return self.cursor.execute(sql, params)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django_prometheus/db/common.py", line 69, in execute
netbox-1               |     return super().execute(*args, **kwargs)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/psycopg/cursor.py", line 97, in execute
netbox-1               |     raise ex.with_traceback(None)
netbox-1               | psycopg.errors.UniqueViolation: duplicate key value violates unique constraint "dcim_interface_unique_device_name"
netbox-1               | DETAIL:  Key (device_id, name)=(500, Network) already exists.
netbox-1               |
netbox-1               | The above exception was the direct cause of the following exception:
netbox-1               |
netbox-1               | Traceback (most recent call last):
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
netbox-1               |     response = get_response(request)
netbox-1               |                ^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/core/handlers/base.py", line 197, in _get_response
netbox-1               |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
netbox-1               |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/views/decorators/csrf.py", line 65, in _view_wrapper
netbox-1               |     return view_func(request, *args, **kwargs)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/viewsets.py", line 125, in view
netbox-1               |     return self.dispatch(request, *args, **kwargs)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/netbox/netbox/api/viewsets/__init__.py", line 140, in dispatch
netbox-1               |     return super().dispatch(request, *args, **kwargs)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/views.py", line 515, in dispatch
netbox-1               |     response = self.handle_exception(exc)
netbox-1               |                ^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/views.py", line 475, in handle_exception
netbox-1               |     self.raise_uncaught_exception(exc)
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/views.py", line 486, in raise_uncaught_exception
netbox-1               |     raise exc
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/views.py", line 512, in dispatch
netbox-1               |     response = handler(request, *args, **kwargs)
netbox-1               |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/mixins.py", line 19, in create
netbox-1               |     self.perform_create(serializer)
netbox-1               |   File "/opt/netbox/netbox/netbox/api/viewsets/__init__.py", line 174, in perform_create
netbox-1               |     instance = serializer.save()
netbox-1               |                ^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/serializers.py", line 210, in save
netbox-1               |     self.instance = self.create(validated_data)
netbox-1               |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/netbox/netbox/api/serializers/features.py", line 32, in create
netbox-1               |     instance = super().create(validated_data)
netbox-1               |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/serializers.py", line 991, in create
netbox-1               |     instance = ModelClass._default_manager.create(**validated_data)
netbox-1               |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method
netbox-1               |     return getattr(self.get_queryset(), name)(*args, **kwargs)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/query.py", line 679, in create
netbox-1               |     obj.save(force_insert=True, using=self.db)
netbox-1               |   File "/opt/netbox/netbox/dcim/models/devices.py", line 1291, in save
netbox-1               |     component_model.objects.bulk_create(create_instances)
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method
netbox-1               |     return getattr(self.get_queryset(), name)(*args, **kwargs)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/query.py", line 835, in bulk_create
netbox-1               |     returned_columns = self._batched_insert(
netbox-1               |                        ^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/query.py", line 1875, in _batched_insert
netbox-1               |     self._insert(
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/query.py", line 1847, in _insert
netbox-1               |     return query.get_compiler(using=using).execute_sql(returning_fields)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/sql/compiler.py", line 1836, in execute_sql
netbox-1               |     cursor.execute(sql, params)
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 122, in execute
netbox-1               |     return super().execute(sql, params)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 79, in execute
netbox-1               |     return self._execute_with_wrappers(
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 92, in _execute_with_wrappers
netbox-1               |     return executor(sql, params, many, context)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 100, in _execute
netbox-1               |     with self.db.wrap_database_errors:
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/utils.py", line 91, in __exit__
netbox-1               |     raise dj_exc_value.with_traceback(traceback) from exc_value
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 105, in _execute
netbox-1               |     return self.cursor.execute(sql, params)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/django_prometheus/db/common.py", line 69, in execute
netbox-1               |     return super().execute(*args, **kwargs)
netbox-1               |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
netbox-1               |   File "/opt/netbox/venv/lib/python3.12/site-packages/psycopg/cursor.py", line 97, in execute
netbox-1               |     raise ex.with_traceback(None)
netbox-1               | django.db.utils.IntegrityError: duplicate key value violates unique constraint "dcim_interface_unique_device_name"
netbox-1               | DETAIL:  Key (device_id, name)=(500, Network) already exists.

Additional Context:

This appears to be related to how NetBox handles interface templates from module types. When creating multiple modules from the same module type on the same device, the interfaces are created with identical names, causing the constraint violation.

It's related not only to Interface but also a PowerPowert and other Component

Originally created by @Kani999 on GitHub (May 6, 2025). ### Deployment Type Self-hosted ### NetBox Version v4.2.8 ### Python Version 3.12 ### Steps to Reproduce I'm encountering a 500 Internal Server Error when trying to create multiple modules of the same type on the same device using the NetBox API. When creating a module, NetBox appears to automatically create associated interfaces. If two modules of the same type are added to the same device, NetBox tries to create interfaces with identical names, which violates the unique constraint `dcim_interface_unique_device_name`. ### Steps to Reproduce 1. Have a device with at least two module bays 2. Have a module type that includes interface templates 3. Create a module in the first module bay 4. Try to create another module of the same type in the second module bay ### pynetbox script to reproduce the error: ```python import pynetbox import random import string import time # Replace with your NetBox URL and token NETBOX_URL = "https://your-netbox-instance.example.com" API_TOKEN = "your-api-token" # Initialize PyNetbox API nb = pynetbox.api( NETBOX_URL, token=API_TOKEN ) def random_string(length=8): """Generate a random string of fixed length.""" return ''.join(random.choices(string.ascii_letters + string.digits, k=length)) def setup_and_reproduce_issue(): """ Set up a complete test environment and reproduce the interface duplication issue. This function will: 1. Create a manufacturer 2. Create a device type with module bays 3. Create a site and device role 4. Create a device 5. Create a module type with interface templates 6. Create modules that will trigger the error """ # Generate a random prefix for unique naming for every run prefix = f"test-{random_string(6)}" # Step 1: Create a manufacturer print("Creating manufacturer...") manufacturer = nb.dcim.manufacturers.create( name=f"{prefix}-manufacturer", slug=f"{prefix}-manufacturer" ) print(f"Created manufacturer: {manufacturer.name}") # Step 2: Create a device type with module bays print("Creating device type...") device_type = nb.dcim.device_types.create( manufacturer=manufacturer.id, model=f"{prefix}-device-type", slug=f"{prefix}-device-type" ) print(f"Created device type: {device_type.model}") # Create module bay templates print("Creating module bay templates...") module_bay_template1 = nb.dcim.module_bay_templates.create( device_type=device_type.id, name="Module Bay 1" ) module_bay_template2 = nb.dcim.module_bay_templates.create( device_type=device_type.id, name="Module Bay 2" ) print(f"Created module bay templates") # Step 3: Create a site print("Creating site...") site = nb.dcim.sites.create( name=f"{prefix}-site", slug=f"{prefix}-site", status="active" ) print(f"Created site: {site.name}") # Create a device role print("Creating device role...") device_role = nb.dcim.device_roles.create( name=f"{prefix}-role", slug=f"{prefix}-role", color="ff0000" ) print(f"Created device role: {device_role.name}") # Step 4: Create a device print("Creating device...") device = nb.dcim.devices.create( name=f"{prefix}-device", device_type=device_type.id, site=site.id, role=device_role.id, # Added the required role field status="active" ) print(f"Created device: {device.name}") # Wait for module bays to be created print("Waiting for module bays to be created...") time.sleep(2) # Get the module bays module_bays = list(nb.dcim.module_bays.filter(device_id=device.id)) if len(module_bays) < 2: print(f"Error: Expected 2 module bays, but found {len(module_bays)}") return module_bay1 = module_bays[0] module_bay2 = module_bays[1] print(f"Found module bays: {module_bay1.name}, {module_bay2.name}") # Step 5: Create a module type with interface templates print("Creating module type...") module_type = nb.dcim.module_types.create( manufacturer=manufacturer.id, model=f"{prefix}-module-type", part_number=random_string(8) ) print(f"Created module type: {module_type.model}") # Create an interface template for the module type print("Creating interface template...") interface_template = nb.dcim.interface_templates.create( module_type=module_type.id, name="Network", type="100base-tx" ) print(f"Created interface template: {interface_template.name}") # Step 6: Create first module (should succeed) print("\nCreating first module...") try: module1 = nb.dcim.modules.create( device=device.id, module_bay=module_bay1.id, module_type=module_type.id, name="Test Module" ) print(f"Successfully created first module with ID: {module1.id}") except Exception as e: print(f"Error creating first module: {e}") return # Step 7: Create second module (should fail with 500 error) print("\nCreating second module (this should fail with a 500 error)...") try: module2 = nb.dcim.modules.create( device=device.id, module_bay=module_bay2.id, module_type=module_type.id, name="Test Module 2" # Even with a different name, it will fail ) print(f"Created second module with ID: {module2.id}") except Exception as e: print(f"ERROR ENCOUNTERED (expected): {e}") print("\nThis error demonstrates the issue with NetBox: When creating multiple modules") print("of the same type on the same device, NetBox tries to create interfaces with") print("identical names, violating the unique constraint for interface names per device.") raise e if __name__ == "__main__": setup_and_reproduce_issue() ``` ### Expected Behavior When creating multiple modules of the same type on the same device, NetBox should: - Either generate unique interface names for each module - Or append a suffix/numbering scheme to ensure uniqueness - Or provide a way to customize interface names during module creation - Or return a proper Error code instead of 500 ### Observed Behavior ### Error Details Script output: ``` Creating manufacturer... Created manufacturer: test-C9NaMQ-manufacturer Creating device type... Created device type: test-C9NaMQ-device-type Creating module bay templates... Created module bay templates Creating site... Created site: test-C9NaMQ-site Creating device role... Created device role: test-C9NaMQ-role Creating device... Created device: test-C9NaMQ-device Waiting for module bays to be created... Found module bays: Module Bay 1, Module Bay 2 Creating module type... Created module type: test-C9NaMQ-module-type Creating interface template... Created interface template: Network Creating first module... Successfully created first module with ID: 1962 Creating second module (this should fail with a 500 error)... ERROR ENCOUNTERED (expected): The request failed with code 500 Internal Server Error but more specific details were not returned in json. Check the NetBox Logs or investigate this exception's error attribute. This error demonstrates the issue with NetBox: When creating multiple modules of the same type on the same device, NetBox tries to create interfaces with identical names, violating the unique constraint for interface names per device. Traceback (most recent call last): File "/Users/jankrupa/Work/pynetbox_api_scripts/my_scripts/power_port_duplicate_error_500.py", line 169, in <module> setup_and_reproduce_issue() ~~~~~~~~~~~~~~~~~~~~~~~~~^^ File "/Users/jankrupa/Work/pynetbox_api_scripts/my_scripts/power_port_duplicate_error_500.py", line 156, in setup_and_reproduce_issue raise e File "/Users/jankrupa/Work/pynetbox_api_scripts/my_scripts/power_port_duplicate_error_500.py", line 144, in setup_and_reproduce_issue module2 = nb.dcim.modules.create( device=device.id, ...<2 lines>... name="Test Module 2" # Even with a different name, it will fail ) File "/Users/jankrupa/Work/pynetbox_api_scripts/pynetbox/core/endpoint.py", line 417, in create ).post(args[0] if args else kwargs) ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/jankrupa/Work/pynetbox_api_scripts/pynetbox/core/query.py", line 370, in post return self._make_call(verb="post", data=data) ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/jankrupa/Work/pynetbox_api_scripts/pynetbox/core/query.py", line 260, in _make_call raise RequestError(req) pynetbox.core.query.RequestError: The request failed with code 500 Internal Server Error but more specific details were not returned in json. Check the NetBox Logs or investigate this exception's error attribute. ``` The error from PostgreSQL: ``` ERROR: duplicate key value violates unique constraint "dcim_interface_unique_device_name" DETAIL: Key (device_id, name)=(device_id, interface_name) already exists. ``` ### NetBox Log ``` netbox-1 | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/manufacturers/ HTTP/1.1" 201 388 "-" "python-requests/2.32.3" netbox-1 | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/device-types/ HTTP/1.1" 201 1142 "-" "python-requests/2.32.3" netbox-1 | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/module-bay-templates/ HTTP/1.1" 201 704 "-" "python-requests/2.32.3" netbox-1 | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/module-bay-templates/ HTTP/1.1" 201 704 "-" "python-requests/2.32.3" netbox-1 | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/sites/ HTTP/1.1" 201 649 "-" "python-requests/2.32.3" netbox-1 | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/device-roles/ HTTP/1.1" 201 414 "-" "python-requests/2.32.3" netbox-1 | XXXX - - [06/May/2025:12:57:07 +0000] "POST /api/dcim/devices/ HTTP/1.1" 201 2252 "-" "python-requests/2.32.3" netbox-1 | XXXX - - [06/May/2025:12:57:09 +0000] "GET /api/dcim/module-bays/?device_id=500&limit=0 HTTP/1.1" 200 1145 "-" "python-requests/2.32.3" netbox-1 | XXXX - - [06/May/2025:12:57:09 +0000] "POST /api/dcim/module-types/ HTTP/1.1" 201 650 "-" "python-requests/2.32.3" netbox-1 | XXXX - - [06/May/2025:12:57:09 +0000] "POST /api/dcim/interface-templates/ HTTP/1.1" 201 806 "-" "python-requests/2.32.3" netbox-1 | XXXX - - [06/May/2025:12:57:10 +0000] "POST /api/dcim/modules/ HTTP/1.1" 201 1162 "-" "python-requests/2.32.3" postgres-1 | 2025-05-06 12:57:10.178 UTC [11072] ERROR: duplicate key value violates unique constraint "dcim_interface_unique_device_name" postgres-1 | 2025-05-06 12:57:10.178 UTC [11072] DETAIL: Key (device_id, name)=(500, Network) already exists. postgres-1 | 2025-05-06 12:57:10.178 UTC [11072] STATEMENT: INSERT INTO "dcim_interface" ("created", "last_updated", "custom_field_data", "device_id", "name", "label", "description", "module_id", "cable_id", "cable_end", "mark_connected", "_path_id", "enabled", "mtu", "mode", "parent_id", "bridge_id", "untagged_vlan_id", "qinq_svlan_id", "vlan_translation_policy_id", "primary_mac_address_id", "_name", "lag_id", "type", "mgmt_only", "speed", "duplex", "wwn", "rf_role", "rf_channel", "rf_channel_frequency", "rf_channel_width", "tx_power", "poe_mode", "poe_type", "wireless_link_id", "vrf_id") VALUES ('2025-05-06 12:57:10.177735+00:00'::timestamptz, '2025-05-06 12:57:10.177749+00:00'::timestamptz, '{"ignore_importer": false, "review": false}'::jsonb, 500, 'Network', '', '', 1963, NULL, NULL, false, NULL, true, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '9999999999999999Network..................', NULL, '100base-tx', false, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) RETURNING "dcim_interface"."id" netbox-1 | Internal Server Error: /api/dcim/modules/ netbox-1 | Traceback (most recent call last): netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 105, in _execute netbox-1 | return self.cursor.execute(sql, params) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django_prometheus/db/common.py", line 69, in execute netbox-1 | return super().execute(*args, **kwargs) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/psycopg/cursor.py", line 97, in execute netbox-1 | raise ex.with_traceback(None) netbox-1 | psycopg.errors.UniqueViolation: duplicate key value violates unique constraint "dcim_interface_unique_device_name" netbox-1 | DETAIL: Key (device_id, name)=(500, Network) already exists. netbox-1 | netbox-1 | The above exception was the direct cause of the following exception: netbox-1 | netbox-1 | Traceback (most recent call last): netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner netbox-1 | response = get_response(request) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/core/handlers/base.py", line 197, in _get_response netbox-1 | response = wrapped_callback(request, *callback_args, **callback_kwargs) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/views/decorators/csrf.py", line 65, in _view_wrapper netbox-1 | return view_func(request, *args, **kwargs) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/viewsets.py", line 125, in view netbox-1 | return self.dispatch(request, *args, **kwargs) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/netbox/netbox/api/viewsets/__init__.py", line 140, in dispatch netbox-1 | return super().dispatch(request, *args, **kwargs) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/views.py", line 515, in dispatch netbox-1 | response = self.handle_exception(exc) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/views.py", line 475, in handle_exception netbox-1 | self.raise_uncaught_exception(exc) netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/views.py", line 486, in raise_uncaught_exception netbox-1 | raise exc netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/views.py", line 512, in dispatch netbox-1 | response = handler(request, *args, **kwargs) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/mixins.py", line 19, in create netbox-1 | self.perform_create(serializer) netbox-1 | File "/opt/netbox/netbox/netbox/api/viewsets/__init__.py", line 174, in perform_create netbox-1 | instance = serializer.save() netbox-1 | ^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/serializers.py", line 210, in save netbox-1 | self.instance = self.create(validated_data) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/netbox/netbox/api/serializers/features.py", line 32, in create netbox-1 | instance = super().create(validated_data) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/rest_framework/serializers.py", line 991, in create netbox-1 | instance = ModelClass._default_manager.create(**validated_data) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method netbox-1 | return getattr(self.get_queryset(), name)(*args, **kwargs) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/query.py", line 679, in create netbox-1 | obj.save(force_insert=True, using=self.db) netbox-1 | File "/opt/netbox/netbox/dcim/models/devices.py", line 1291, in save netbox-1 | component_model.objects.bulk_create(create_instances) netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method netbox-1 | return getattr(self.get_queryset(), name)(*args, **kwargs) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/query.py", line 835, in bulk_create netbox-1 | returned_columns = self._batched_insert( netbox-1 | ^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/query.py", line 1875, in _batched_insert netbox-1 | self._insert( netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/query.py", line 1847, in _insert netbox-1 | return query.get_compiler(using=using).execute_sql(returning_fields) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/models/sql/compiler.py", line 1836, in execute_sql netbox-1 | cursor.execute(sql, params) netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 122, in execute netbox-1 | return super().execute(sql, params) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 79, in execute netbox-1 | return self._execute_with_wrappers( netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 92, in _execute_with_wrappers netbox-1 | return executor(sql, params, many, context) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 100, in _execute netbox-1 | with self.db.wrap_database_errors: netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/utils.py", line 91, in __exit__ netbox-1 | raise dj_exc_value.with_traceback(traceback) from exc_value netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django/db/backends/utils.py", line 105, in _execute netbox-1 | return self.cursor.execute(sql, params) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/django_prometheus/db/common.py", line 69, in execute netbox-1 | return super().execute(*args, **kwargs) netbox-1 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ netbox-1 | File "/opt/netbox/venv/lib/python3.12/site-packages/psycopg/cursor.py", line 97, in execute netbox-1 | raise ex.with_traceback(None) netbox-1 | django.db.utils.IntegrityError: duplicate key value violates unique constraint "dcim_interface_unique_device_name" netbox-1 | DETAIL: Key (device_id, name)=(500, Network) already exists. ``` --- Additional Context: This appears to be related to how NetBox handles interface templates from module types. When creating multiple modules from the same module type on the same device, the interfaces are created with identical names, causing the constraint violation. It's related not only to Interface but also a PowerPowert and other `Component`
adam added the type: bug label 2025-12-29 21:40:42 +01:00
adam closed this issue 2025-12-29 21:40:43 +01:00
Author
Owner

@arthanson commented on GitHub (May 6, 2025):

@Kani999 You can create the interface with the token {module} which will get replaced when you create the module. So in your code something like:

# Create an interface template for the module type
    print("Creating interface template...")
    interface_template = nb.dcim.interface_templates.create(
        module_type=module_type.id,
        name="Network-{module}",
        type="100base-tx"
    )
    print(f"Created interface template: {interface_template.name}")

This allows you to set how you want the interface name created based on the slot number. Closing as not a bug.

@arthanson commented on GitHub (May 6, 2025): @Kani999 You can create the interface with the token `{module}` which will get replaced when you create the module. So in your code something like: ``` # Create an interface template for the module type print("Creating interface template...") interface_template = nb.dcim.interface_templates.create( module_type=module_type.id, name="Network-{module}", type="100base-tx" ) print(f"Created interface template: {interface_template.name}") ``` This allows you to set how you want the interface name created based on the slot number. Closing as not a bug.
Author
Owner

@Kani999 commented on GitHub (May 7, 2025):

@arthanson
I understand how to use the {module}, but still I think this should not raise an error 500. Instead, it should raise an error 400 or something similar with a 'ValidationError'.

@Kani999 commented on GitHub (May 7, 2025): @arthanson I understand how to use the `{module}`, but still I think this should not raise an error 500. Instead, it should raise an error 400 or something similar with a 'ValidationError'.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#11128