HumanSignal/label-studio · error · ValidationError

It's expected to have 'email' field in 'completed_by' data i

Error message

It's expected to have 'email' field in 'completed_by' data in annotations

What it means

Raised in BaseTaskSerializerBulk._insert_valid_completed_by when an imported annotation's completed_by is a dict but lacks an 'email' key (legacy FF-off path). completed_by must be either an email dict, a known member ID, or None; the library requires the dict form to carry 'email' so it can map the annotator to a user in the importing organization.

Source

Thrown at label_studio/tasks/serializers.py:598

        for annotation in annotations:
            completed_by = annotation.get('completed_by')

            if use_fallback:
                resolved_id = resolve_completed_by_id(completed_by, members_email_to_id, members_ids, default_user.id)
                if resolved_id is not None:
                    annotation['completed_by_id'] = resolved_id
                annotation.pop('completed_by', None)
                continue

            # --- legacy FF-off branches -------------------------------------------------
            # no completed_by info found - just skip it, will be assigned to the user who imports
            if completed_by is None:
                annotation['completed_by_id'] = default_user.id

            # resolve annotators by email
            elif isinstance(completed_by, dict):
                if 'email' not in completed_by:
                    raise ValidationError("It's expected to have 'email' field in 'completed_by' data in annotations")

                email = completed_by['email']
                if email not in members_email_to_id:
                    if settings.ALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS:
                        annotation['completed_by_id'] = default_user.id
                    else:
                        raise ValidationError(f"Unknown annotator's email {email}")
                else:
                    # overwrite an actual member ID
                    annotation['completed_by_id'] = members_email_to_id[email]

            # old style annotators specification - try to find them by ID
            elif isinstance(completed_by, int) and completed_by in members_ids:
                annotation['completed_by_id'] = completed_by

            # in any other cases - import validation error
            else:
                raise ValidationError(

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Include the annotator's email in the dict: {"completed_by": {"email": "annotator@example.com"}}
  2. Ensure the annotator email belongs to a member of the target project's organization (register/invite them first)
  3. Or use the legacy integer form with a known member ID: {"completed_by": 5}
  4. Or omit completed_by entirely (it defaults to the importing user)
  5. Enable the BROS-1092 fallback feature flag so unknown completed_by values are re-attributed to the default user instead of raising

Example fix

// before
{"annotations": [{"result": [], "completed_by": {"user_id": 5}}]}
// after
{"annotations": [{"result": [], "completed_by": {"email": "annotator@example.com"}}]}
Defensive patterns

Strategy: validation

Validate before calling

def check_completed_by(annotations):
    for a in annotations:
        cb = a.get('completed_by')
        if isinstance(cb, dict) and 'email' not in cb:
            raise ValueError(f"completed_by dict missing 'email': {cb}")

Type guard

def has_email_completed_by(a):
    cb = a.get('completed_by')
    return not isinstance(cb, dict) or 'email' in cb

Try / catch

from rest_framework.exceptions import ValidationError
try:
    client.import_tasks(project_id, tasks)
except ValidationError as e:
    if "expected to have 'email' field" in str(e.detail):
        for t in tasks:
            for a in t.get('annotations', []):
                cb = a.get('completed_by')
                if isinstance(cb, dict) and 'email' not in cb:
                    a['completed_by'] = None  # fall back to importing user
    else:
        raise

Prevention

When it happens

Trigger: Importing tasks with annotations like {"completed_by": {"id": 5}} or {"completed_by": {"user": "x@y.com"}} — a dict without 'email' — while feature flag fflag_fix_back_bros_1092_import_unknown_completed_by_short is disabled and create() runs during task import.

Common situations: Hand-converted exports where completed_by was reshaped but 'email' renamed or dropped; exports from systems that key annotators by username or ID; migrating annotations between Label Studio instances with customized export scripts.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29). Data as JSON: /api/errors/6e7bd542ae9043f8. Report an issue: GitHub.