HumanSignal/label-studio · warning · ValidationError

empty

empty

Error message

empty

What it means

Raised by TaskListSerializer.to_internal_value when the submitted list is empty and allow_empty is False. A POST with an empty array of tasks yields the non-field error 'empty' (code empty); if the serializer is a partial child of a parent serializer, SkipField is raised instead.

Source

Thrown at label_studio/tasks/serializers.py:533

            code = f' {detail[0].code}' if detail[0].code != 'invalid' else ''
            return f'Error{code} at item {i}: {detail[0]} :: {item}'
        else:
            errors = ', '.join(detail)
            codes = [d.code for d in detail]
            return f'Errors {codes} at item {i}: {errors} :: {item}'

    def to_internal_value(self, data):
        """Body of run_validation for all data items"""
        if data is None:
            raise ValidationError('All tasks are empty (None)')

        if not isinstance(data, list):
            raise ValidationError({api_settings.NON_FIELD_ERRORS_KEY: 'not a list'}, code='not_a_list')

        if not self.allow_empty and len(data) == 0:
            if self.parent and self.partial:
                raise SkipField()
            raise ValidationError({api_settings.NON_FIELD_ERRORS_KEY: 'empty'}, code='empty')

        ret, errors = [], []
        self.annotation_count, self.prediction_count = 0, 0
        for i, item in enumerate(data):
            try:
                validated = self.child.validate(item)
            except ValidationError as exc:
                error = self.format_error(i, exc.detail, item)
                errors.append(error)
                # do not print to user too many errors
                if len(errors) >= 100:
                    errors[99] = '...'
                    break
            else:
                ret.append(validated)
                errors.append({})

                if 'annotations' in item:

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Only call the import API when you have at least one task; skip the request entirely if the list is empty
  2. Check upstream data extraction (file/CSV/query) for empty results and fail early with a clearer message
  3. If empty imports should be legal for your workflow, relax the empty-data guard before sending or adjust the serializer usage
  4. Add client-side logging to distinguish 'no data fetched' from 'import failed'

Example fix

// before
await fetch(`/api/projects/${id}/import`, { method: 'POST', body: JSON.stringify(tasks) });
// after
if (tasks.length === 0) return; // nothing to import
await fetch(`/api/projects/${id}/import`, { method: 'POST', body: JSON.stringify(tasks) });
Defensive patterns

Strategy: validation

Validate before calling

if (Array.isArray(tasks) && tasks.length === 0) {
  console.info('no tasks to import, skipping API call');
  return;
}

Type guard

function isNonEmptyTaskList(v) {
  return Array.isArray(v) && v.length > 0;
}

Try / catch

try {
  await importTasks(projectId, tasks);
} catch (e) {
  if (JSON.stringify(e.response?.data || {}).includes('"empty"')) {
    console.warn('Empty task list rejected — check upstream data source');
  } else throw e;
}

Prevention

When it happens

Trigger: POST to /api/projects/<id>/import or /api/tasks/ with a zero-length JSON array ([]) while the serializer disallows empty input.

Common situations: Upstream pipeline produced no rows (empty CSV/JSON file); a filter or transformation removed all tasks before submission; re-running an import script after data was already consumed; sync jobs posting deltas that happen to be empty.

Related errors


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