HumanSignal/label-studio · error · ValidationError
Unknown annotator's email {email}
Error message
Unknown annotator's email {email} What it means
Raised in _insert_valid_completed_by when an annotation's completed_by dict contains an 'email' that is not found in the target organization's member email→id map and settings.ALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS is False (legacy FF-off path). The import is rejected because the annotator cannot be resolved to a user in the destination project's organization.
Source
Thrown at label_studio/tasks/serializers.py:605
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(
f"Import data contains completed_by={completed_by} which is not a valid annotator's email or ID"
)
annotation.pop('completed_by', None)
@staticmethod
def _insert_valid_user_reviews(dicts, members_email_to_id, default_user):
"""Insert correct user id by email from snapshotView on GitHub (pinned to 0b49e9b539)
Solutions
- Invite/register the annotator email as a member of the target organization before importing
- Set ALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS=true (env) so unknown emails fall back to the importing user
- Remove or rewrite completed_by in the annotations to the importing user's email
- Enable the BROS-1092 fallback feature flag (fflag_fix_back_bros_1092_import_unknown_completed_by_short) which re-attributes unknown annotators to default_user instead of raising
- Check email casing/whitespace — the lookup is an exact dict key match
Example fix
// before (env) ALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS=false # unknown email 'alice@old.com' -> 400 // after ALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS=true # falls back to importing user
Defensive patterns
Strategy: validation
Validate before calling
def check_annotator_emails(tasks, org_member_emails):
unknown = {a['completed_by']['email']
for t in tasks for a in t.get('annotations', [])
if isinstance(a.get('completed_by'), dict) and 'email' in a['completed_by']
} - org_member_emails
if unknown:
raise ValueError(f'Emails not in target org: {sorted(unknown)}') Type guard
def email_is_org_member(a, member_emails):
cb = a.get('completed_by')
return not (isinstance(cb, dict) and 'email' in cb) or cb['email'] in member_emails Try / catch
from rest_framework.exceptions import ValidationError
try:
client.import_tasks(project_id, tasks)
except ValidationError as e:
if "Unknown annotator's email" in str(e.detail):
# remap unknown annotators to the importing user, or set
# ALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS=true and retry
for t in tasks:
for a in t.get('annotations', []):
if isinstance(a.get('completed_by'), dict) and a['completed_by']['email'] not in member_emails:
a['completed_by'] = None
else:
raise Prevention
- Invite all annotators to the target organization before importing their annotations
- Fetch org members (GET /api/organizations/<id>/members) and diff against export emails before import
- Normalize email casing/whitespace in both export and org membership
- Set ALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS=true for cross-instance migrations
When it happens
Trigger: Importing an export snapshot into a different Label Studio instance/organization where the original annotator's email is not a member; emails changed or case-mismatched; inviting annotators after the import attempt.
Common situations: Cross-instance project migration; test-to-production environment moves; re-importing annotations from an org whose members were removed; typo'd or differently-cased emails in the export.
Related errors
- It's expected to have 'email' field in 'completed_by' data i
- Import data contains completed_by={completed_by} which is no
- Prediction validation failed ({len(validation_errors)} error
- "url" is not found in request data
- "url" must be 2048 characters or fewer
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/a5b65fc1e6ab6716.
Report an issue: GitHub.