HumanSignal/label-studio · error · ValidationError

Unsupported or invalid JSON structure

Error message

Unsupported or invalid JSON structure

What it means

The streaming JSON reader accepts only a top-level JSON array, a single object, or line-delimited objects; anything else (scalar, bare string array handled elsewhere, malformed nesting) hits the fall-through branch and raises this ValidationError.

Source

Thrown at label_studio/data_import/models.py:211

                        formatted_task = self._format_task_for_json_streaming(task)
                        batch.append(formatted_task)
                        if len(batch) >= batch_size:
                            yield batch
                            batch = []

                elif first_byte == ord('{'):
                    # Single JSON object: parse once and yield a single-item batch
                    raw_data = file_handle.read()
                    try:
                        task_data = json.loads(raw_data)
                    except TypeError:
                        task_data = json.loads(raw_data.decode('utf8'))
                    formatted_task = self._format_task_for_json_streaming(task_data)
                    batch.append(formatted_task)

                else:
                    # Unknown/invalid JSON structure
                    raise ValidationError('Unsupported or invalid JSON structure')

                # Yield remaining tasks if any
                if batch:
                    yield batch

        except Exception as exc:
            raise ValidationError(f'Failed to parse JSON file {self.file_name}: {extract_message(exc)}')

    def _format_task_for_json_streaming(self, task):
        """Format task data for JSON streaming consistency with read_tasks_list_from_json"""
        # Handle different task types as in the original read_tasks_list_from_json method
        if isinstance(task, dict):
            if not task.get('data'):
                task = {'data': task}
        else:
            # If task is not a dict (e.g., list), wrap it in {'data': task}
            task = {'data': task}

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Wrap the content as a top-level JSON array of objects, e.g. [{"data": {...}}]
  2. Unwrap any envelope key so the file's root is the tasks array itself
  3. Validate the file parses to an array or object before uploading

Example fix

// before (tasks.json)
{"items": [{"text": "a"}]}
// after
[{"data": {"text": "a"}}]
Defensive patterns

Strategy: validation

Validate before calling

import json
with open('tasks.json', encoding='utf8') as f:
    root = json.load(f)
if not isinstance(root, (list, dict)):
    raise ValueError("Top level must be an array or object")
if isinstance(root, dict):
    root = [root]
assert all(isinstance(t, (dict,)) for t in root), "All tasks must be objects"

Type guard

def is_streamable_json_root(value):
    if isinstance(value, list):
        return all(isinstance(t, dict) for t in value)
    return isinstance(value, dict)

Try / catch

try:
    for batch in fu.read_tasks_streaming():
        process(batch)
except ValidationError as e:
    logger.error("Streaming import rejected file: %s", e)

Prevention

When it happens

Trigger: read_tasks_streaming on a .json upload whose parsed top level is a scalar (e.g. 42, "text"), or an unsupported container shape not matched by the parser's known cases.

Common situations: Uploading a JSON file that is a single number/string; files exported with an envelope object like {"items": [...]} that the reader does not unwrap; corrupted or hand-edited JSON exports.

Understand the failure class

Related errors


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