Server error during API validation when required custom field is empty #1222

Closed
opened 2025-12-29 16:30:12 +01:00 by adam · 3 comments
Owner

Originally created by @arionl on GitHub (Sep 15, 2017).

Issue type

[ ] Feature request
[X] Bug report
[ ] Documentation

Environment

  • Python version: 3.6.2
  • NetBox version: 2.1.4

Description

If I try to manipulate an object via the API (a Site in my case) that has a custom field of type "Selection", the PUT request results in an error even if I'm not changing the contents of the custom field.

Example:
Site "1" named "Home" with two custom fields:

  • Customtext
  • Customselection (options are: 'one', 'two', 'three')
curl -H "Authorization: Token XXX" "Accept: application/json; indent=4" http://netbox/api/dcim/sites/1/
{"id":1,"name":"Home","slug":"home","region":{"id":2,"url":"http://netbox/api/dcim/regions/2/","name":"USA","slug":"usa"},"tenant":null,"facility":"Home","asn":55,"physical_address":"Vienna, VA","shipping_address":"","contact_name":"Arion","contact_phone":"","contact_email":"","comments":"Testing","custom_fields":{"CustomSelection":{"value":4,"label":"one"},"CustomText":"My custom text"},"count_prefixes":5,"count_vlans":0,"count_racks":2,"count_devices":13,"count_circuits":1}

Changing something like the ASN field (from 55 to 56) and then attempting to resubmit:

curl -X PUT -H "Authorization: Token XXX" -H "Content-Type: application/json" -H "Accept: application/json; indent=4" http://netbox/api/dcim/sites/1/ --data '{"id":1,"name":"Home","slug":"home","region":2,"tenant":null,"facility":"Home","asn":56,"physical_address":"Vienna, VA","shipping_address":"","contact_name":"Arion","contact_phone":"","contact_email":"","comments":"Testing","custom_fields":{"CustomSelection":{"value":4,"label":"one"},"CustomText":"My custom text"},"count_prefixes":5,"count_vlans":0,"count_racks":2,"count_devices":13,"count_circuits":1}'

..results in:

<pre><strong>&lt;class &#39;TypeError&#39;&gt;</strong><br />
int() argument must be a string, a bytes-like object or a number, not &#39;dict&#39;</pre>

I also tried a Site that had both custom fields set to 'null' and same result. If I remove the "Customselection" custom field completely, everything works as intended.

I first stumbled upon this problem because I got an error while trying to use the pynetbox library. Working backwards it seems like the raw PUT request gives an error. I believe this is a NetBox API issue not pynetbox, but I'm still a bit fuzzy and learning. Hope I'm reporting this properly.

Originally created by @arionl on GitHub (Sep 15, 2017). <!-- Before opening a new issue, please search through the existing issues to see if your topic has already been addressed. Note that you may need to remove the "is:open" filter from the search bar to include closed issues. Check the appropriate type for your issue below by placing an x between the brackets. If none of the below apply, please raise your issue for discussion on our mailing list: https://groups.google.com/forum/#!forum/netbox-discuss Please note that issues which do not fall under any of the below categories will be closed. ---> ### Issue type [ ] Feature request <!-- Requesting the implementation of a new feature --> [X] Bug report <!-- Reporting unexpected or erroneous behavior --> [ ] Documentation <!-- Proposing a modification to the documentation --> <!-- Please describe the environment in which you are running NetBox. (Be sure to verify that you are running the latest stable release of NetBox before submitting a bug report.) --> ### Environment * Python version: 3.6.2 * NetBox version: 2.1.4 <!-- BUG REPORTS must include: * A list of the steps needed to reproduce the bug * A description of the expected behavior * Any relevant error messages (screenshots may also help) FEATURE REQUESTS must include: * A detailed description of the proposed functionality * A use case for the new feature * A rough description of any necessary changes to the database schema * Any relevant third-party libraries which would be needed --> ### Description If I try to manipulate an object via the API (a Site in my case) that has a custom field of type "Selection", the PUT request results in an error even if I'm not changing the contents of the custom field. Example: Site "1" named "Home" with two custom fields: - Customtext - Customselection (options are: 'one', 'two', 'three') ``` curl -H "Authorization: Token XXX" "Accept: application/json; indent=4" http://netbox/api/dcim/sites/1/ {"id":1,"name":"Home","slug":"home","region":{"id":2,"url":"http://netbox/api/dcim/regions/2/","name":"USA","slug":"usa"},"tenant":null,"facility":"Home","asn":55,"physical_address":"Vienna, VA","shipping_address":"","contact_name":"Arion","contact_phone":"","contact_email":"","comments":"Testing","custom_fields":{"CustomSelection":{"value":4,"label":"one"},"CustomText":"My custom text"},"count_prefixes":5,"count_vlans":0,"count_racks":2,"count_devices":13,"count_circuits":1} ``` Changing something like the ASN field (from 55 to 56) and then attempting to resubmit: ``` curl -X PUT -H "Authorization: Token XXX" -H "Content-Type: application/json" -H "Accept: application/json; indent=4" http://netbox/api/dcim/sites/1/ --data '{"id":1,"name":"Home","slug":"home","region":2,"tenant":null,"facility":"Home","asn":56,"physical_address":"Vienna, VA","shipping_address":"","contact_name":"Arion","contact_phone":"","contact_email":"","comments":"Testing","custom_fields":{"CustomSelection":{"value":4,"label":"one"},"CustomText":"My custom text"},"count_prefixes":5,"count_vlans":0,"count_racks":2,"count_devices":13,"count_circuits":1}' ``` ..results in: ``` <pre><strong>&lt;class &#39;TypeError&#39;&gt;</strong><br /> int() argument must be a string, a bytes-like object or a number, not &#39;dict&#39;</pre> ``` I also tried a Site that had both custom fields set to 'null' and same result. If I remove the "Customselection" custom field completely, everything works as intended. I first stumbled upon this problem because I got an error while trying to use the [pynetbox](https://github.com/digitalocean/pynetbox) library. Working backwards it seems like the raw PUT request gives an error. I believe this is a NetBox API issue not pynetbox, but I'm still a bit fuzzy and learning. Hope I'm reporting this properly.
adam added the type: bug label 2025-12-29 16:30:12 +01:00
adam closed this issue 2025-12-29 16:30:12 +01:00
Author
Owner

@huzichunjohn commented on GitHub (Sep 15, 2017):

@arionl : hi, try to modify your custom fields in request like below. i try and it works for me.
"custom_fields": {"CustomSelection": 4, "CustomText": "My custom text"}

@huzichunjohn commented on GitHub (Sep 15, 2017): @arionl : hi, try to modify your custom fields in request like below. i try and it works for me. "custom_fields": {"CustomSelection": 4, "CustomText": "My custom text"}
Author
Owner

@arionl commented on GitHub (Sep 15, 2017):

Ah, if I change:

"custom_fields":{"CustomSelection":{"value":7,"label":"one"},"CustomText":"My custom text"}

..to:

"custom_fields":{"CustomSelection":7,"CustomText":"My custom text"}

the PUT request works. However, this custom field isn't marked as required so if it doesn't have a value (null) or I try to set it back to null, I then get the 'int' error:

"custom_fields":{"CustomSelection":null,"CustomText":"My custom text"}
@arionl commented on GitHub (Sep 15, 2017): Ah, if I change: ``` "custom_fields":{"CustomSelection":{"value":7,"label":"one"},"CustomText":"My custom text"} ``` ..to: ``` "custom_fields":{"CustomSelection":7,"CustomText":"My custom text"} ``` the PUT request works. However, this custom field isn't marked as *required* so if it doesn't have a value (null) or I try to set it back to null, I then get the 'int' error: ``` "custom_fields":{"CustomSelection":null,"CustomText":"My custom text"} ```
Author
Owner

@huzichunjohn commented on GitHub (Sep 18, 2017):

--- a/netbox/extras/api/customfields.py
+++ b/netbox/extras/api/customfields.py
@@ -36,27 +36,37 @@ class CustomFieldsSerializer(serializers.BaseSerializer):
                 raise ValidationError("Invalid custom field for {} objects: {}".format(content_type, field_name))
 
             # Validate boolean
-            if cf.type == CF_TYPE_BOOLEAN and value not in [True, False, 1, 0]:
-                raise ValidationError("Invalid value for boolean field {}: {}".format(field_name, value))
+            if cf.type == CF_TYPE_BOOLEAN:
+               if value and value not in [True, False, 1, 0]:
+                    raise ValidationError("Invalid value for boolean field {}: {}".format(field_name, value))
+               elif not value and cf.required:
+                   raise ValidationError("{} is required field and can't be None, you should pass a bool value.".format(field_nam
 
             # Validate date
             if cf.type == CF_TYPE_DATE:
-                try:
-                    datetime.strptime(value, '%Y-%m-%d')
-                except ValueError:
-                    raise ValidationError("Invalid date for field {}: {}. (Required format is YYYY-MM-DD.)".format(
-                        field_name, value
-                    ))
+                if value:
+                    try:
+                        datetime.strptime(value, '%Y-%m-%d')
+                    except ValueError:
+                        raise ValidationError("Invalid date for field {}: {}. (Required format is YYYY-MM-DD.)".format(
+                            field_name, value
+                        ))
+               elif not value and cf.required:
+                    raise ValidationError("{} is required field and can't be None, you should pass a date.".format(field_name))
 
             # Validate selected choice
             if cf.type == CF_TYPE_SELECT:
-                try:
-                    value = int(value)
-                except ValueError:
-                    raise ValidationError("{}: Choice selections must be passed as integers.".format(field_name))
-                valid_choices = [c.pk for c in cf.choices.all()]
-                if value not in valid_choices:
-                    raise ValidationError("Invalid choice for field {}: {}".format(field_name, value))
+               if value:
+                    try:
+                        value = int(value)
+                    except ValueError:
+                        raise ValidationError("{}: Choice selections must be passed as integers.".format(field_name))
+                   valid_choices = [c.pk for c in cf.choices.all()]
+                    if value not in valid_choices:
+                        raise ValidationError("Invalid choice for field {}: {}".format(field_name, value))
+
+               elif not value and cf.required:
+                   raise ValidationError("{} is required field and can't be None, you should pass a integer.".format(field_name))
 
         # Check for missing required fields
         missing_fields = []
@huzichunjohn commented on GitHub (Sep 18, 2017): ```diff --- a/netbox/extras/api/customfields.py +++ b/netbox/extras/api/customfields.py @@ -36,27 +36,37 @@ class CustomFieldsSerializer(serializers.BaseSerializer): raise ValidationError("Invalid custom field for {} objects: {}".format(content_type, field_name)) # Validate boolean - if cf.type == CF_TYPE_BOOLEAN and value not in [True, False, 1, 0]: - raise ValidationError("Invalid value for boolean field {}: {}".format(field_name, value)) + if cf.type == CF_TYPE_BOOLEAN: + if value and value not in [True, False, 1, 0]: + raise ValidationError("Invalid value for boolean field {}: {}".format(field_name, value)) + elif not value and cf.required: + raise ValidationError("{} is required field and can't be None, you should pass a bool value.".format(field_nam # Validate date if cf.type == CF_TYPE_DATE: - try: - datetime.strptime(value, '%Y-%m-%d') - except ValueError: - raise ValidationError("Invalid date for field {}: {}. (Required format is YYYY-MM-DD.)".format( - field_name, value - )) + if value: + try: + datetime.strptime(value, '%Y-%m-%d') + except ValueError: + raise ValidationError("Invalid date for field {}: {}. (Required format is YYYY-MM-DD.)".format( + field_name, value + )) + elif not value and cf.required: + raise ValidationError("{} is required field and can't be None, you should pass a date.".format(field_name)) # Validate selected choice if cf.type == CF_TYPE_SELECT: - try: - value = int(value) - except ValueError: - raise ValidationError("{}: Choice selections must be passed as integers.".format(field_name)) - valid_choices = [c.pk for c in cf.choices.all()] - if value not in valid_choices: - raise ValidationError("Invalid choice for field {}: {}".format(field_name, value)) + if value: + try: + value = int(value) + except ValueError: + raise ValidationError("{}: Choice selections must be passed as integers.".format(field_name)) + valid_choices = [c.pk for c in cf.choices.all()] + if value not in valid_choices: + raise ValidationError("Invalid choice for field {}: {}".format(field_name, value)) + + elif not value and cf.required: + raise ValidationError("{} is required field and can't be None, you should pass a integer.".format(field_name)) # Check for missing required fields missing_fields = [] ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#1222