{"record":{"id":"8cacdc27f8c97c4a","repo":"HumanSignal/label-studio","slug":"import-data-contains-completed-by-completed-by-w","errorCode":null,"errorMessage":"Import data contains completed_by={completed_by} which is not a valid annotator's email or ID","messagePattern":"Import data contains completed_by=(.+?) which is not a valid annotator's email or ID","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"label_studio/tasks/serializers.py","lineNumber":616,"sourceCode":"                    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\n\n        :param dicts: draft or review dicts from snapshot\n        :param members_email_to_id: mapping from emails to current LS instance user IDs\n        :param default_user: if email is not found in membr_email_to_id, this user will be used\n        :return:\n        \"\"\"\n        for obj in dicts:\n            created_by = obj.get('created_by', {})\n            email = created_by.get('email') if isinstance(created_by, dict) else None\n\n            # user default user","sourceCodeStart":598,"sourceCodeEnd":634,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/tasks/serializers.py#L598-L634","documentation":"Raised in _insert_valid_completed_by as the catch-all branch: the annotation's completed_by value is neither None, a dict with 'email', nor an integer that matches a current organization member ID (legacy FF-off path). It means the import payload contains a completed_by of an unsupported type or an unknown integer ID, so the annotator cannot be resolved.","triggerScenarios":"Importing annotations with completed_by set to a string like \"5\", a float, a list, an integer ID that no longer exists in the organization (users deleted or a different instance), or any other non-None/non-dict/non-member-int value during task import.","commonSituations":"Exports carried over from older Label Studio versions or other tools with stringified user IDs; importing into a fresh instance where old user IDs don't exist; JSON round-trips that turned ints into strings.","solutions":["Convert completed_by to an email dict form: {\"completed_by\": {\"email\": \"annotator@example.com\"}} with that email in the target org","If using integer IDs, verify the ID exists in the target organization's members (GET /api/organizations/<id>/members) and use that ID","Remove completed_by from annotations so it defaults to the importing user","Coerce string IDs back to real ints in the payload before import","Enable the BROS-1092 fallback feature flag so unresolvable values silently re-attribute to the default user"],"exampleFix":"// before\n{\"annotations\": [{\"completed_by\": \"12\"}]}        // string, not int -> rejected\n// after\n{\"annotations\": [{\"completed_by\": {\"email\": \"annotator@example.com\"}}]}","handlingStrategy":"validation","validationCode":"def check_completed_by_types(tasks, valid_member_ids):\n    for t in tasks:\n        for a in t.get('annotations', []):\n            cb = a.get('completed_by')\n            ok = cb is None or (isinstance(cb, dict) and 'email' in cb) or (isinstance(cb, int) and not isinstance(cb, bool) and cb in valid_member_ids)\n            if not ok:\n                raise ValueError(f'Unresolvable completed_by: {cb!r}')","typeGuard":"def is_resolvable_completed_by(cb, member_ids):\n    if cb is None: return True\n    if isinstance(cb, dict): return 'email' in cb\n    if isinstance(cb, int) and not isinstance(cb, bool): return cb in member_ids\n    return False","tryCatchPattern":"from rest_framework.exceptions import ValidationError\ntry:\n    client.import_tasks(project_id, tasks)\nexcept ValidationError as e:\n    if \"not a valid annotator's email or ID\" in str(e.detail):\n        for t in tasks:\n            for a in t.get('annotations', []):\n                cb = a.get('completed_by')\n                if isinstance(cb, str) and cb.isdigit():\n                    a['completed_by'] = int(cb)   # coerce stringified IDs\n                elif not isinstance(cb, dict):\n                    a['completed_by'] = None      # default to importing user\n    else:\n        raise","preventionTips":["Never stringify user IDs in import payloads","Convert legacy exports to the email-dict completed_by form","Verify integer IDs against the target org's current members before import"],"tags":["django","import","completed-by","validation","type-mismatch"],"backgroundTag":"invalid-completed-by-value","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}