{"record":{"id":"5fc0060c1ad532b2","repo":"HumanSignal/label-studio","slug":"annotation-result-field-in-annotation-must-be-li","errorCode":null,"errorMessage":"annotation \"result\" field in annotation must be list","messagePattern":"annotation \"result\" field in annotation must be list","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"label_studio/tasks/serializers.py","lineNumber":202,"sourceCode":"                'UNIQUE constraint failed: task_completion.unique_id',\n                'duplicate key value violates unique constraint \"task_completion_unique_id_key\"',\n            ]\n            if any([error in str(e) for error in errors]):\n                raise AnnotationDuplicateError()\n            raise\n\n    def validate_result(self, value):\n        data = value\n        # convert from str to json if need\n        if isinstance(value, str):\n            try:\n                data = json.loads(value)\n            except:  # noqa: E722\n                raise ValueError('annotation \"result\" can\\'t be parse from str to JSON')\n\n        # check result is list\n        if not isinstance(data, list):\n            raise ValidationError('annotation \"result\" field in annotation must be list')\n\n        # FIT-1669: collapse `(id, from_name, type)` collisions at the write boundary\n        # so the annotation record never persists duplicate-id rows.\n        return dedupe_annotation_result_list(data)\n\n    def _resolve_project_for_validation(self, data):\n        if 'task' in data:\n            return data['task'].project\n        if self.instance is not None:\n            return self.instance.project\n        task = self.context.get('task')\n        if task is not None:\n            return task.project\n        return None\n\n    def validate(self, data):\n        \"\"\"Validate annotation result against project config and custom interface output_schema.\"\"\"\n        if 'result' not in data or data.get('was_cancelled') is True:","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/tasks/serializers.py#L184-L220","documentation":"AnnotationSerializer.validate_result() requires the (possibly string-parsed) annotation 'result' to be a list of region objects. If the parsed value is a dict, string, number, or null instead of a list, DRF ValidationError is raised with this message. Valid results are lists that are then deduped by (id, from_name, type).","triggerScenarios":"Posting an annotation where result is {\"key\": ...} (a single region object not wrapped in a list), a bare string like \"ok\", or null — via the annotations API or AnnotationSerializer.","commonSituations":"Clients sending one region object directly instead of [region]; APIs that return a dict being echoed back as a result; result:null from failed pipelines; confusing the prediction 'value' dict format with the annotation result list format.","solutions":["Wrap single region objects in a list: result=[region_dict]","Ensure the value is JSON null-free and is an array of region objects, each with id/from_name/type/value","Check upstream code that may unwrap lists (e.g. result['result'][0]) and resend the full list","Validate client-side: isinstance(result, list) and all(isinstance(r, dict) for r in result)"],"exampleFix":"// before\n{\"result\": {\"id\": 1, \"from_name\": \"label\", \"type\": \"labels\", \"value\": {\"labels\": [\"cat\"]}}}\n// after\n{\"result\": [{\"id\": 1, \"from_name\": \"label\", \"type\": \"labels\", \"value\": {\"labels\": [\"cat\"]}}]}","handlingStrategy":"type-guard","validationCode":"def assert_result_is_region_list(result):\n    data = json.loads(result) if isinstance(result, str) else result\n    if not isinstance(data, list) or not all(isinstance(r, dict) for r in data):\n        raise ValueError('annotation result must be a list of region dicts')","typeGuard":"def is_region_list(value) -> bool:\n    import json\n    data = json.loads(value) if isinstance(value, str) else value\n    return isinstance(data, list) and all(isinstance(r, dict) for r in data)","tryCatchPattern":"from rest_framework.exceptions import ValidationError\ntry:\n    ser = AnnotationSerializer(data=payload)\n    ser.is_valid(raise_exception=True)\nexcept ValidationError as e:\n    logger.error(\"annotation result must be a list: %s\", e.detail)","preventionTips":["Always wrap even a single region in a list before POSTing annotations","Don't confuse prediction 'value' dicts with the annotation result list format","Assert result is a non-null list in client code before serialization","Round-trip test your annotation payloads through json.loads to confirm shapes"],"tags":["annotation","validation","serializer","format"],"backgroundTag":"invalid-prediction-result-format","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}