{"record":{"id":"a5b65fc1e6ab6716","repo":"HumanSignal/label-studio","slug":"unknown-annotator-s-email-email","errorCode":null,"errorMessage":"Unknown annotator's email {email}","messagePattern":"Unknown annotator's email (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"label_studio/tasks/serializers.py","lineNumber":605,"sourceCode":"                annotation.pop('completed_by', None)\n                continue\n\n            # --- legacy FF-off branches -------------------------------------------------\n            # no completed_by info found - just skip it, will be assigned to the user who imports\n            if completed_by is None:\n                annotation['completed_by_id'] = default_user.id\n\n            # resolve annotators by email\n            elif isinstance(completed_by, dict):\n                if 'email' not in completed_by:\n                    raise ValidationError(\"It's expected to have 'email' field in 'completed_by' data in annotations\")\n\n                email = completed_by['email']\n                if email not in members_email_to_id:\n                    if settings.ALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS:\n                        annotation['completed_by_id'] = default_user.id\n                    else:\n                        raise ValidationError(f\"Unknown annotator's email {email}\")\n                else:\n                    # overwrite an actual member ID\n                    annotation['completed_by_id'] = members_email_to_id[email]\n\n            # old style annotators specification - try to find them by ID\n            elif isinstance(completed_by, int) and completed_by in members_ids:\n                annotation['completed_by_id'] = completed_by\n\n            # in any other cases - import validation error\n            else:\n                raise ValidationError(\n                    f\"Import data contains completed_by={completed_by} which is not a valid annotator's email or ID\"\n                )\n            annotation.pop('completed_by', None)\n\n    @staticmethod\n    def _insert_valid_user_reviews(dicts, members_email_to_id, default_user):\n        \"\"\"Insert correct user id by email from snapshot","sourceCodeStart":587,"sourceCodeEnd":623,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/tasks/serializers.py#L587-L623","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (env)\nALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS=false  # unknown email 'alice@old.com' -> 400\n// after\nALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS=true   # falls back to importing user","handlingStrategy":"validation","validationCode":"def check_annotator_emails(tasks, org_member_emails):\n    unknown = {a['completed_by']['email']\n               for t in tasks for a in t.get('annotations', [])\n               if isinstance(a.get('completed_by'), dict) and 'email' in a['completed_by']\n               } - org_member_emails\n    if unknown:\n        raise ValueError(f'Emails not in target org: {sorted(unknown)}')","typeGuard":"def email_is_org_member(a, member_emails):\n    cb = a.get('completed_by')\n    return not (isinstance(cb, dict) and 'email' in cb) or cb['email'] in member_emails","tryCatchPattern":"from rest_framework.exceptions import ValidationError\ntry:\n    client.import_tasks(project_id, tasks)\nexcept ValidationError as e:\n    if \"Unknown annotator's email\" in str(e.detail):\n        # remap unknown annotators to the importing user, or set\n        # ALLOW_IMPORT_TASKS_WITH_UNKNOWN_EMAILS=true and retry\n        for t in tasks:\n            for a in t.get('annotations', []):\n                if isinstance(a.get('completed_by'), dict) and a['completed_by']['email'] not in member_emails:\n                    a['completed_by'] = None\n    else:\n        raise","preventionTips":["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"],"tags":["django","import","completed-by","organization-members","unknown-email"],"backgroundTag":"unknown-annotator-email","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}