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 snapshot

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Invite/register the annotator email as a member of the target organization before importing
  2. Set ALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS=true (env) so unknown emails fall back to the importing user
  3. Remove or rewrite completed_by in the annotations to the importing user's email
  4. 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
  5. 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

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


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