{"record":{"id":"7e894643a0955b3d","repo":"HumanSignal/label-studio","slug":"load-tasks-data-root-must-be-list","errorCode":null,"errorMessage":"load_tasks: Data root must be list","messagePattern":"load_tasks: Data root must be list","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"label_studio/data_import/uploader.py","lineNumber":235,"sourceCode":"        else:\n            could_be_tasks_list = False\n            (\n                data_keys,\n                found_formats,\n                tasks,\n                file_upload_ids,\n                could_be_tasks_list,\n            ) = tasks_from_url(file_upload_ids, project_import.project, user, url, could_be_tasks_list)\n            if could_be_tasks_list:\n                project_import.could_be_tasks_list = True\n                project_import.save(update_fields=['could_be_tasks_list'])\n\n    elif project_import.tasks:\n        tasks = project_import.tasks\n\n    # check is data root is list\n    if not isinstance(tasks, list):\n        raise ValidationError('load_tasks: Data root must be list')\n\n    # empty tasks error\n    if not tasks:\n        raise ValidationError('load_tasks: No tasks added')\n\n    check_max_task_number(tasks)\n    return tasks, file_upload_ids, found_formats, list(data_keys)\n\n\ndef load_tasks_for_async_import_streaming(project_import, user, batch_size=1000):\n    \"\"\"Load tasks from different types of request.data / request.files saved in project_import model,\n    yielding tasks in batches to reduce memory usage\"\"\"\n    from django.conf import settings\n\n    if not batch_size:\n        batch_size = settings.IMPORT_BATCH_SIZE\n\n    all_file_upload_ids = []","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/data_import/uploader.py#L217-L253","documentation":"ValidationError raised by load_tasks_for_async_import in label_studio/data_import/uploader.py. After resolving tasks from uploaded files, URL, or inline project_import.tasks, the data root must be a JSON list. If the parsed payload is a dict or scalar, the async import aborts with 'load_tasks: Data root must be list'.","triggerScenarios":"async_import_background -> load_tasks_for_async_import where the imported JSON file/payload parses to an object (e.g. {\"tasks\": [...]} or a single dict) instead of a top-level array.","commonSituations":"Uploading a JSON file shaped as {\"data\": {...}} or a config-style object; exporting a single task as an object; user pastes JSON with a wrapper key; migrating from another tool that emits object-wrapped arrays.","solutions":["Reformat the JSON so the top level is an array: [{...}, {...}].","If your data is {\"tasks\": [...]}, upload only the inner array (save data['tasks'] to a new file).","Wrap a single record in a list before importing.","If a converter produced the wrapper, fix the export settings to emit a bare records array."],"exampleFix":"// before (rejected)\n{\"tasks\": [{\"data\": {\"text\": \"hi\"}}]}\n\n// after (accepted)\n[{\"data\": {\"text\": \"hi\"}}]","handlingStrategy":"type-guard","validationCode":"import json\npayload = json.load(open(path))\nif not isinstance(payload, list):\n    # unwrap common wrappers\n    payload = payload.get('tasks', [payload] if isinstance(payload, dict) else None)\njson.dump(payload, open('import.json', 'w'))","typeGuard":"def is_task_list(payload):\n    return isinstance(payload, list) and all(isinstance(t, dict) for t in payload)","tryCatchPattern":"try:\n    import_tasks(payload)\nexcept ValidationError as e:\n    if 'Data root must be list' in str(e):\n        payload = [payload] if isinstance(payload, dict) else payload['tasks']\n        import_tasks(payload)\n    else:\n        raise","preventionTips":["Always export/import JSON arrays: [{\"data\": {...}}, ...].","Unwrap keys like {\"tasks\": [...]} or {\"items\": [...]} before import.","Validate with json.load + isinstance(payload, list) before calling the API.","Test one small file through the import endpoint before large uploads."],"tags":["django","validation","json-shape","import-format","label-studio"],"backgroundTag":"invalid-json-root-type","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}