{"record":{"id":"21b85fb596efb18b","repo":"HumanSignal/label-studio","slug":"field-data-must-be-string-or-dict-but-not-typ","errorCode":null,"errorMessage":"Field \"data\" must be string or dict, but not \"{type(self.instance.data)}\"","messagePattern":"Field \"data\" must be string or dict, but not \"(.+?)\"","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"label_studio/tasks/validation.py","lineNumber":150,"sourceCode":"\n    def validate(self, task):\n        \"\"\"Validate whole task with task['data'] and task['annotations']. task['predictions']\"\"\"\n        # task is class\n        if hasattr(task, 'data'):\n            self.check_data_and_root(self.project, task.data)\n            return task\n\n        # self.instance is loaded by get_object of view\n        if self.instance and hasattr(self.instance, 'data'):\n            if isinstance(self.instance.data, dict):\n                data = self.instance.data\n            elif isinstance(self.instance.data, str):\n                try:\n                    data = json.loads(self.instance.data)\n                except ValueError as e:\n                    raise ValidationError(\"Can't parse task data: \" + extract_message(e))\n            else:\n                raise ValidationError(\n                    'Field \"data\" must be string or dict, but not \"' + type(self.instance.data) + '\"'\n                )\n            self.check_data_and_root(self.instance.project, data)\n            return task\n\n        # check task is dict\n        if not isinstance(task, dict):\n            raise ValidationError('Task root must be dict with \"data\", \"meta\", \"annotations\", \"predictions\" fields')\n\n        # task[data] | task[annotations] | task[predictions] | task[meta]\n        if self.check_allowed(task):\n            # task[data]\n            self.raise_if_wrong_class(task, 'data', (dict, list))\n            self.check_data_and_root(self.project, task['data'])\n\n            # task[annotations]: we can't use AnnotationSerializer for validation\n            # because it's much different with validation we need here\n            self.raise_if_wrong_class(task, 'annotations', list)","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/tasks/validation.py#L132-L168","documentation":"Also in the instance-validation branch of validate(): if Task.instance.data is neither a dict nor a string, this ValidationError fires, embedding the actual Python type name. Task.data is normally a JSONField (dict) or a JSON string; any other stored type indicates a model-level anomaly.","triggerScenarios":"instance.data is e.g. a list stored in a non-JSON field, an int/bytes value from manual DB manipulation, or a custom subclass overriding the data field, reaching validate() during serializer run_validation/to_internal_value.","commonSituations":"Manual database edits or migrations writing raw Python objects; ORM misuse assigning non-serializable objects to task.data; older Label Studio versions where data was stored differently than the current code expects (version-upgrade artifacts).","solutions":["Normalize task.data to a dict (or JSON string) and save the instance","Inspect the offending task row and correct the column type/content","Check for code paths that assign non-dict/non-str values to task.data","After version upgrades, run a migration/backfill ensuring Task.data is valid JSON dict or string"],"exampleFix":"// before\ntask.data = b'{\"text\": \"hi\"}'  # bytes\n// after\ntask.data = json.loads(b'{\"text\": \"hi\"}')  # dict","handlingStrategy":"type-guard","validationCode":"def data_field_ok(task):\n    return isinstance(task.data, (dict, str))\nbad = [t.id for t in Task.objects.all() if not data_field_ok(t)]","typeGuard":"def task_data_is_valid_type(task):\n    return hasattr(task, 'data') and isinstance(task.data, (dict, str))","tryCatchPattern":"try:\n    validator.validate(task)\nexcept ValidationError as e:\n    if str(e.detail[0]).startswith('Field \"data\" must be string or dict'):\n        task.data = normalize_to_dict(task.data)\n        task.save()","preventionTips":["Never assign raw Python objects/bytes to task.data; assign dicts","Guard ORM writes: assert isinstance(value, (dict, str)) before task.data = value","After migrations/upgrades, sweep Task rows for non-dict/non-str data values"],"tags":["type-mismatch","database","task-structure"],"backgroundTag":"schema-validation-failed","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}