HumanSignal/label-studio · error · ValidationError
Task is empty (None)
Error message
Task is empty (None)
What it means
TaskValidator.check_data raises this DRF ValidationError when the task's data payload is literally None. Label Studio requires every task to carry a data dict containing the fields named by the project's labeling config (e.g. the $text variable's key), so a None payload cannot possibly satisfy any config. It is the first guard in check_data, before config-driven key/type checks run.
Source
Thrown at label_studio/tasks/validation.py:58
'Ranker': [list, str],
}
logger = logging.getLogger(__name__)
class TaskValidator:
"""Task Validator with project scheme configs validation. It is equal to TaskSerializer from django backend."""
def __init__(self, project, instance=None):
self.project = project
self.instance = instance
self.annotation_count = 0
self.prediction_count = 0
@staticmethod
def check_data(project, data):
"""Validate data from task['data']"""
if data is None:
raise ValidationError('Task is empty (None)')
replace_task_data_undefined_with_config_field(data, project)
# iterate over data types from project
for data_key, data_type in project.data_types.items():
# get array name in case of Repeater tag
is_array = '[' in data_key
data_key = data_key.split('[')[0]
if '.' in data_key:
keys = data_key.split('.')
try:
data_item = reduce(getitem, keys, data)
except KeyError:
raise ValidationError('"{data_key}" key is expected in task data'.format(data_key=data_key))
else:
if data_key not in data:
raise ValidationError('"{data_key}" key is expected in task data'.format(data_key=data_key))View on GitHub (pinned to 0b49e9b539)
Solutions
- Replace None data with a dict containing the required config keys, e.g. {'text': ...}
- Filter out null items from the import batch before calling the import/tasks API
- If a record is genuinely empty, skip it server-side rather than submitting it
- Catch ValidationError and report the offending item index to the user
Example fix
// before
client.start_project(label_config=config).import_tasks([{'data': None}])
// after
client.start_project(label_config=config).import_tasks([{'data': {'text': row.get('text', '')}} if row else {'data': {'text': ''}}]) Defensive patterns
Strategy: validation
Validate before calling
def ensure_task_data(items):
bad = [i for i, t in enumerate(items) if t is None or t.get('data') is None]
if bad:
raise ValueError(f'items at {bad} have None data') Type guard
def has_data(item):
return isinstance(item, dict) and item.get('data') is not None Try / catch
from rest_framework.exceptions import ValidationError
try:
validator.validate(item)
except ValidationError as e:
if 'Task is empty' in str(e.detail[0]):
log.warning('skipping null task item') Prevention
- Never submit {'data': None}; omit or skip empty records instead
- Coerce empty DataFrame/CSV rows to {} or drop them pre-import
- Unit-test import pipelines with null-row fixtures
When it happens
Trigger: POST/IMPORT of a task item whose 'data' value is None (e.g. {'data': None} in the tasks list, or an item that is None and gets routed through validate -> check_data_and_root -> check_data).
Common situations: Import scripts building tasks from rows where a source record is null; JSON imports where a line is the literal 'null'; CSV/pandas pipelines where missing rows become None instead of {}; SDK calls passing data=None.
Related errors
- {e.detail[0]} [assume: item as is = task root with values]
- {e.detail[0]} [assume: item["data"] = task root with values]
- Maximum task number is {settings.TASKS_MAX_NUMBER}, current
- Maximum total size of all files is {settings.TASKS_MAX_FILE_
- {ext} extension is not supported
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/ffdd19e4a2137c1a.
Report an issue: GitHub.