HumanSignal/label-studio · error · ValidationError

Task item should be dict

Error message

Task item should be dict

What it means

Label Studio's read_tasks_list_from_json normalizes each imported JSON task so that it has a 'data' key holding a dict. When a task's resolved 'data' payload is not a dict (e.g. a string or number), it raises this ValidationError because tasks must map column keys to values.

Source

Thrown at label_studio/data_import/models.py:166

        return tasks

    def read_tasks_list_from_json(self):
        logger.debug('Read tasks list from JSON file {}'.format(self.filepath))

        raw_data = self.content
        # Python 3.5 compatibility fix https://docs.python.org/3/whatsnew/3.6.html#json
        try:
            tasks = json.loads(raw_data)
        except TypeError:
            tasks = json.loads(raw_data.decode('utf8'))
        if isinstance(tasks, dict):
            tasks = [tasks]
        tasks_formatted = []
        for i, task in enumerate(tasks):
            if not task.get('data'):
                task = {'data': task}
            if not isinstance(task['data'], dict):
                raise ValidationError('Task item should be dict')
            tasks_formatted.append(task)
        return tasks_formatted

    def read_tasks_list_from_json_streaming(self, batch_size=100):
        logger.debug('Read tasks list from JSON file streaming {}'.format(self.filepath))

        try:
            with self.file.open('rb') as file_handle:
                # Peek a small prefix to detect top-level container ('[' array or '{' object)
                sniff = file_handle.read(4096) or b''
                # Find first non-whitespace byte
                first_byte = None
                for b in sniff:
                    if b not in (0x20, 0x09, 0x0A, 0x0D):  # space, tab, lf, cr
                        first_byte = b
                        break

                # Rewind after sniffing

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Wrap each item as an object with a $undefined_key or your data key, e.g. [{"data": {"text": "a"}}]
  2. Re-export the file so each element is a JSON object with key/value fields
  3. Validate the JSON structure before upload (every element is an object)

Example fix

// before (tasks.json)
["first text", "second text"]
// after
[{"data": {"text": "first text"}}, {"data": {"text": "second text"}}]
Defensive patterns

Strategy: validation

Validate before calling

import json
with open('tasks.json') as f:
    tasks = json.load(f)
if isinstance(tasks, dict):
    tasks = [tasks]
for t in tasks:
    data = t.get('data') if isinstance(t, dict) else t
    if not isinstance(data, dict):
        raise ValueError(f"Task data must be a dict, got {type(data).__name__}")

Type guard

def is_valid_task(item):
    if isinstance(item, dict):
        return isinstance(item.get('data'), dict)
    return isinstance(item, dict)
valid = [t for t in tasks if is_valid_task(t)]

Try / catch

from rest_framework.exceptions import ValidationError
try:
    tasks = fu.read_tasks()
except ValidationError as e:
    logger.error("Task structure invalid: %s", e)
    # fix file or skip

Prevention

When it happens

Trigger: Calling read_tasks (via FileUpload.read_tasks or load_tasks_from_uploaded_files) with a .json file whose top-level array (or object) yields elements whose data is a non-dict, e.g. a JSON file containing ["a","b"] where each bare string becomes data.

Common situations: Uploading a JSON file of plain strings or numbers instead of objects; exporting from another tool into a flat JSON array of scalars; hand-edited JSON where the object wrapper was lost.

Related errors


AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29). Data as JSON: /api/errors/240e5635cdd5038a. Report an issue: GitHub.