Repository: paperless-ngx/paperless-ngx

Issue:
[BUG] Removing document link can cause Internal Server Error
### Description

When trying to remove a custom field of the "Document Link" type from a document, if that custom field has never been populated, an Internal Server Error will be thrown (but nothing appears in the logs)

After linking some document, the issue does not arise anymore and the field can be removed again, whether it currently has linked documents or not.

### Steps to reproduce

1. Create a custom field of the document link type
2. Go to a document that does not have that field assigned
3. Assign the custom field to the document and save, but do not actually link another document
4. Try to remove the custom field from the document again

### Webserver logs

```bash
{"headers":{"normalizedNames":{},"lazyUpdate":null},"status":500,"statusText":"OK","url":"https://paperless.mydomain.de/api/documents/1310/","ok":false,"name":"HttpErrorResponse","message":"Http failure response for https://paperless.mydomain.de/api/documents/1310/: 500 OK","error":"Internal Server Error"}
```


### Browser logs

_No response_

### Paperless-ngx version

2.3.3

### Host OS

Synology DS723+ | x86_64

### Installation method

Docker - official image

### Browser

_No response_

### Configuration changes

_No response_

### Other

_No response_

### Please confirm the following

- [X] I believe this issue is a bug that affects all users of Paperless-ngx, not something specific to my installation.
- [X] I have already searched for relevant existing issues and discussions before opening this report.
- [X] I have updated the title field above with a concise description.


Discussion:
I sincerely doubt nothing is shown in the logs.
```
[2024-01-18 06:39:53,396] [ERROR] [django.request] Internal Server Error: /api/documents/374/
Traceback (most recent call last):
  File "lib/python3.10/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "lib/python3.10/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "lib/python3.10/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
    return view_func(*args, **kwargs)
  File "lib/python3.10/site-packages/rest_framework/viewsets.py", line 125, in view
    return self.dispatch(request, *args, **kwargs)
  File "lib/python3.10/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "lib/python3.10/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "lib/python3.10/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "lib/python3.10/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/src/documents/views.py", line 332, in update
    response = super().update(request, *args, **kwargs)
  File "lib/python3.10/site-packages/rest_framework/mixins.py", line 68, in update
    self.perform_update(serializer)
  File "lib/python3.10/site-packages/rest_framework/mixins.py", line 78, in perform_update
    serializer.save()
  File "lib/python3.10/site-packages/drf_writable_nested/mixins.py", line 233, in save
    return super(BaseNestedModelSerializer, self).save(**kwargs)
  File "lib/python3.10/site-packages/rest_framework/serializers.py", line 207, in save
    self.instance = self.update(self.instance, validated_data)
  File "/src/documents/serialisers.py", line 675, in update
    for doc_id in custom_field_instance.value:
TypeError: 'NoneType' object is not iterable
```

I'll fix it in a bit, its because the value is None, not []

Resolving patch:
diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py
--- a/src/documents/serialisers.py
+++ b/src/documents/serialisers.py
@@ -670,7 +670,10 @@ def update(self, instance: Document, validated_data):
             for custom_field_instance in instance.custom_fields.filter(
                 field__data_type=CustomField.FieldDataType.DOCUMENTLINK,
             ):
-                if custom_field_instance.field not in incoming_custom_fields:
+                if (
+                    custom_field_instance.field not in incoming_custom_fields
+                    and custom_field_instance.value is not None
+                ):
                     # Doc link field is being removed entirely
                     for doc_id in custom_field_instance.value:
                         CustomFieldInstanceSerializer.remove_doclink(


Test patch: