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
- Include the annotator's email in the dict: {"completed_by": {"email": "annotator@example.com"}}
- Ensure the annotator email belongs to a member of the target project's organization (register/invite them first)
- Or use the legacy integer form with a known member ID: {"completed_by": 5}
- Or omit completed_by entirely (it defaults to the importing user)
- 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
- Always export annotators as {"email": ...} dicts when hand-writing import JSON
- Normalize export scripts to emit the email-keyed completed_by form
- Prefer omitting completed_by unless correct attribution is required
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
- Import data contains completed_by={completed_by} which is no
- Prediction validation failed ({len(validation_errors)} error
- "url" must be 2048 characters or fewer
- preannotated_fields
- If you use "annotations" field in the task, you must put "da
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/6e7bd542ae9043f8.
Report an issue: GitHub.