HumanSignal / HumanSignal/label-studio
HTTP 500 on /api/dm/project — ProjectSerializer instantiated without context causes KeyError in user_id property
- Dominant language
- TypeScript
- Stars
- 28.3k
- Forks
- 3.7k
- Avg merge
- 14h
- Merged PRs (30d)
- 15
Description
### Describe the bug
`GET /api/dm/project?project={id}` consistently returns HTTP 500 because `ProjectSerializer` is instantiated without a `context` argument in `label_studio/data_manager/api.py`. During serialization, the `queue_total` / `queue_done` SerializerMethodFields invoke the `user_id` property, which expects either `context['request']` or `context['user_cache']` to be present. With both missing, the property raises `KeyError`, which DRF converts to a 500 response.
This breaks the project metadata endpoint used by the Data Manager UI. The task list and labeling itself remain functional (other `/api/dm/*` endpoints are unaffected), but the queue progress counters in the Data Manager header do not display.
### To Reproduce
1. Deploy Label Studio v1.23.0 (Docker image `heartexlabs/label-studio:1.23.0`)
2. Log in and open any project's Data Manager view
3. Open browser DevTools → Network tab
4. Observe `GET /api/dm/project?project=` returning **HTTP 500**
Other endpoints loaded on the same view (`/api/dm/columns`, `/api/dm/views`, `/api/dm/tasks`, `/api/projects/{id}`) return 200 normally.
### Expected behavior
`GET /api/dm/project?project={id}` returns HTTP 200 with the project metadata including `queue_total` and `queue_done` computed against the authenticated user.
### Traceback
```
Traceback (most recent call last):
File "/label-studio/label_studio/projects/serializers.py", line 121, in user_id
return self.context['request'].user.id
~~~~~~~~~~~~^^^^^^^^^^^
KeyError: 'request'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File ".../rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File ".../django/utils/decorators.py", line 48, in _wrapper
return bound_method(*args, **kwargs)
File "/label-studio/label_studio/data_manager/api.py", line 516, in get
data = ProjectSerializer(project).data
File ".../rest_framework/serializers.py", line 571, in data
ret = super().data
File ".../rest_framework/serializers.py", line 249, in data
self._data = self.to_representation(self.instance)
File ".../rest_flex_fields/serializers.py", line 64, in to_representation
return super().to_representation(instance)
File ".../rest_framework/serializers.py", line 538, in to_representation
ret[field.field_name] = field.to_representation(attribute)
File ".../rest_framework/fields.py", line 1870, in to_representation
return method(value)
File "/label-studio/label_studio/projects/serializers.py", line 309, in get_queue_total
Q(is_labeled=False) & ~Q(annotations__completed_by_id=self.user_id)
File "/label-studio/label_studio/projects/serializers.py", line 123, in user_id
return next(iter(self.context['user_cache']))
KeyError: 'user_cache'
```
### Root cause
In `label_studio/data_manager/api.py` (around line 515 in v1.23.0):
```python
def get(self, request):
pk = int_from_request(request.GET, 'project', 1)
project = generics.get_object_or_404(Project, pk=pk)
self.check_object_permissions(request, project)
data = ProjectSerializer(project).data # ← context is not passed
```
The `ProjectSerializer.user_id` property in `label_studio/projects/serializers.py` expects context:
```python
@property
def user_id(self):
try:
return self.context['request'].user.id
except KeyError:
return next(iter(self.context['user_cache']))
```
When `context` is empty, the `KeyError` from `context['request']` is caught, but the fallback to `context['user_cache']` raises another `KeyError` that propagates. `get_queue_total` (around line 304) calls `self.user_id` while building the ORM `Q()` filter, so the exception surfaces during serialization of every `/api/dm/project` request.
### Suggested fix
Pass `request` into the serializer context, consistent with how `ProjectSerializer` is invoked elsewhere in the codebase:
```python
data = ProjectSerializer(project, context={'request': request}).data
```
I'm happy to submit a PR for this — let me know if a PR would be welcome.
### Environment
- **Label Studio version**: 1.23.0
- **Python**: 3.13
- **Deployment**: Docker / Kubernetes (`heartexlabs/label-studio:1.23.0`)
- **Auth method**: standard session/token auth
- **Browser**: any (server-side error)
### Additional context
- All other `/api/dm/*` endpoints on the same Data Manager view return 200
- `/api/projects/{id}` (the non-DM equivalent) also returns 200
- User impact is limited to missing queue progress counters in the Data Manager header — task lists and labeling continue to work, which likely explains why this has gone unreported despite being a regression in the core Data Manager flow
- The fix is a single-line addition; happy to send a PR with a regression test
Contributor guide
Assessment
This issue has not been assessed yet.