Skip to content

fix: pass request context to ProjectSerializer in ProjectStateAPI#9755

Open
caost wants to merge 1 commit into
HumanSignal:developfrom
caost:fix/dm-project-missing-context
Open

fix: pass request context to ProjectSerializer in ProjectStateAPI#9755
caost wants to merge 1 commit into
HumanSignal:developfrom
caost:fix/dm-project-missing-context

Conversation

@caost
Copy link
Copy Markdown

@caost caost commented May 20, 2026

Summary

Fix HTTP 500 on GET /api/dm/project?project={id} when project.created_by is None.

Problem

ProjectStateAPI.get() instantiates ProjectSerializer without passing a request context:

# Before
data = ProjectSerializer(project).data

ProjectSerializer.user_id is called when evaluating the queue_total / queue_done SerializerMethodFields:

@property
def user_id(self):
    try:
        return self.context['request'].user.id  # KeyError when no context
    except KeyError:
        return next(iter(self.context['user_cache']))  # fallback

Why this doesn't always crash (hidden fragility)

In ProjectSerializer.Meta.fields, created_by (index 16) is serialized before queue_total (index 43). UserSimpleSerializer.to_representation() writes the project creator into self.context['user_cache'], which mutates the shared root context dict in-place. This accidentally satisfies the user_id fallback in most cases.

Why it crashes when created_by is None

When project.created_by is None, DRF short-circuits and sets ret['created_by'] = None without ever calling UserSimpleSerializer.to_representation(). As a result, user_cache is never populated and user_id raises KeyError: 'user_cache'HTTP 500.

Silent wrong-value bug (also fixed)

Even for projects with a valid created_by, the old code computed queue_total / queue_done against the project creator's user ID rather than the requesting user's ID.

Fix

# After
data = ProjectSerializer(project, context={'request': request}).data

Passing request explicitly means user_id always resolves from the authenticated request user, regardless of whether created_by is set.

Traceback (reported on v1.23.0)

File "label_studio/data_manager/api.py", line 516, in get
    data = ProjectSerializer(project).data
File "label_studio/projects/serializers.py", in user_id
    return self.context['request'].user.id
KeyError: 'request'

During handling of the above exception, another exception occurred:

File "label_studio/projects/serializers.py", in user_id
    return next(iter(self.context['user_cache']))
KeyError: 'user_cache'

Test Plan

  • test_dm_project_returns_200 — endpoint returns 200
  • test_dm_project_includes_queue_fields — response includes integer queue_total and queue_done
  • test_dm_project_on_freshly_created_project — empty project returns queue_total=0, queue_done=0
  • test_dm_project_returns_404_for_missing_project — unknown project id returns 404
  • test_dm_project_null_creator_returns_200project with created_by=None returns 200 instead of 500 (key regression test)

🤖 Generated with Claude Code

ProjectSerializer was instantiated without context in
data_manager/api.py, causing HTTP 500 on GET /api/dm/project when
project.created_by is None.

Root cause: the user_id property falls back to context['user_cache'],
which is populated as a side-effect of UserSimpleSerializer serializing
the created_by field. When created_by is None, DRF skips that
serializer, user_cache is never written, and user_id raises
KeyError: 'user_cache'.

Even for projects with a creator the old code was silently wrong:
queue_total / queue_done were calculated against the project creator's
user ID instead of the requesting user's ID.

Fix: pass context={'request': request} so user_id always resolves from
the authenticated request.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@caost caost requested a review from a team as a code owner May 20, 2026 13:18
@netlify
Copy link
Copy Markdown

netlify Bot commented May 20, 2026

👷 Deploy request for label-studio-docs-new-theme pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit fae03de

@netlify
Copy link
Copy Markdown

netlify Bot commented May 20, 2026

👷 Deploy request for heartex-docs pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit fae03de

@netlify
Copy link
Copy Markdown

netlify Bot commented May 20, 2026

Deploy Preview for label-studio-storybook canceled.

Name Link
🔨 Latest commit fae03de
🔍 Latest deploy log https://app.netlify.com/projects/label-studio-storybook/deploys/6a0db4b96de6020008db63b0

@netlify
Copy link
Copy Markdown

netlify Bot commented May 20, 2026

Deploy Preview for label-studio-playground canceled.

Name Link
🔨 Latest commit fae03de
🔍 Latest deploy log https://app.netlify.com/projects/label-studio-playground/deploys/6a0db4b988bb0d0008077897

@github-actions github-actions Bot added the fix label May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant