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
- Wrap the content as a top-level JSON array of objects, e.g. [{"data": {...}}]
- Unwrap any envelope key so the file's root is the tasks array itself
- 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
- Keep the file's root as a JSON array of objects
- Avoid envelope objects like {"items": [...]}
- Lint the JSON with python -m json.tool before import
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Task item should be dict
- Failed to parse JSON file {self.file_name}: {extract_message
- Your label config has more than one data key and direct file
- annotation "result" can't be parse from str to JSON
- not_a_list
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/209bf4f0ff2022e3.
Report an issue: GitHub.