{"record":{"id":"2616f45f25e680f3","repo":"HumanSignal/label-studio","slug":"each-item-in-prediction-result-should-be-dict","errorCode":null,"errorMessage":"Each item in prediction result should be dict","messagePattern":"Each item in prediction result should be dict","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"label_studio/tasks/models.py","lineNumber":1126,"sourceCode":"        return timesince(self.created_at)\n\n    def has_permission(self, user):\n        user.project = self.project  # link for activity log\n        return self.project.has_permission(user)\n\n    @classmethod\n    def prepare_prediction_result(cls, result, project):\n        \"\"\"\n        This function does the following logic of transforming \"result\" object:\n        result is list -> use raw result as is\n        result is dict -> put result under single \"value\" section\n        result is string -> find first occurrence of single-valued tag (Choices, TextArea, etc.) and put string under corresponding single field (e.g. \"choices\": [\"my_label\"])  # noqa\n        \"\"\"\n        if isinstance(result, list):\n            # full representation of result\n            for item in result:\n                if not isinstance(item, dict):\n                    raise ValidationError('Each item in prediction result should be dict')\n            # TODO: check consistency with project.label_config\n            return result\n\n        elif isinstance(result, dict):\n            # \"value\" from result\n            # TODO: validate value fields according to project.label_config\n            for tag, tag_info in project.get_parsed_config().items():\n                tag_type = tag_info['type'].lower()\n                if tag_type in result:\n                    return [\n                        {\n                            'from_name': tag,\n                            'to_name': ','.join(tag_info['to_name']),\n                            'type': tag_type,\n                            'value': result,\n                        }\n                    ]\n","sourceCodeStart":1108,"sourceCodeEnd":1144,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/tasks/models.py#L1108-L1144","documentation":"Prediction.prepare_prediction_result() accepts the prediction result either as a full list representation or as a dict ('value' style). When a list is given, every element must be a dict (a region object with id/from_name/type/value etc.). If any element is a scalar, string, or list, ValidationError is raised because the result shape is invalid.","triggerScenarios":"Calling Prediction.objects.create(...), task.add_predictions(...) or the predictions API with result=[\"label-a\", ...] or result=[['x'], {...}] — i.e. a list whose items are not dicts — instead of the required [{...}, {...}] format.","commonSituations":"ML backend returning a flat list of labels that is passed through unchanged; hand-written scripts building predictions from raw model output; confusing the compact single-value format (string) with the full list format; JSON where items were serialized as arrays instead of objects.","solutions":["Wrap each item as a full result dict: {\"id\": <unique>, \"from_name\": <control tag name>, \"to_name\": <source>, \"type\": <tag type>, \"value\": {...}}","If you only have a single value (e.g. one label string), pass result as a plain string/dict instead of a list and let prepare_prediction_result build the region","Validate client-side before sending: all(isinstance(item, dict) for item in result)","Inspect what your model backend returns and convert its output to Label Studio prediction format in a wrapper"],"exampleFix":"// before\nPrediction.objects.create(task=task, result=[\"cat\", \"dog\"])\n// after\nPrediction.objects.create(task=task, result=[\n    {\"id\": 1, \"from_name\": \"label\", \"to_name\": \"image\", \"type\": \"choices\", \"value\": {\"choices\": [\"cat\"]}}\n])","handlingStrategy":"type-guard","validationCode":"def validate_prediction_result(result):\n    if isinstance(result, list):\n        bad = [i for i, item in enumerate(result) if not isinstance(item, dict)]\n        if bad:\n            raise ValueError(f\"result items at indexes {bad} must be dicts\")\n    return result","typeGuard":"def is_full_result_format(result) -> bool:\n    return isinstance(result, list) and all(isinstance(item, dict) for item in result)","tryCatchPattern":"from rest_framework.exceptions import ValidationError\ntry:\n    prediction = Prediction.objects.create(task=task, result=raw_output)\nexcept ValidationError as e:\n    logger.error(\"invalid prediction result %r: %s\", raw_output, e)\n    prediction = None  # convert backend output to LS format and retry","preventionTips":["Convert raw ML backend output to Label Studio region dicts in a wrapper before creating predictions","Remember: list format => every item is a dict; compact format => plain string/scalar, not a list of scalars","Unit-test your prediction-creation code with realistic backend outputs","Validate results against the project label config before submission"],"tags":["prediction","validation","format","serializer"],"backgroundTag":"invalid-prediction-result-format","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}