HumanSignal/label-studio · error · ValidationError

Annotation must have "result" fields

Error message

Annotation must have "result" fields

What it means

TaskValidator.validate in label_studio/tasks/validation.py raises this DRF ValidationError when a task item's 'annotations' list contains a dict entry that has no 'result' key. Label Studio's task import format requires every annotation to carry a 'result' array holding the labeling results, so an annotation without it is considered malformed and the whole import batch is rejected.

Source

Thrown at label_studio/tasks/validation.py:176

            raise ValidationError('Task root must be dict with "data", "meta", "annotations", "predictions" fields')

        # task[data] | task[annotations] | task[predictions] | task[meta]
        if self.check_allowed(task):
            # task[data]
            self.raise_if_wrong_class(task, 'data', (dict, list))
            self.check_data_and_root(self.project, task['data'])

            # task[annotations]: we can't use AnnotationSerializer for validation
            # because it's much different with validation we need here
            self.raise_if_wrong_class(task, 'annotations', list)
            for annotation in task.get('annotations', []):
                if not isinstance(annotation, dict):
                    logger.warning('Annotation must be dict, but "%s" found', str(type(annotation)))
                    continue

                ok = 'result' in annotation
                if not ok:
                    raise ValidationError('Annotation must have "result" fields')

                # check result is list
                if not isinstance(annotation.get('result', []), list):
                    raise ValidationError('"result" field in annotation must be list')

            # task[predictions]
            self.raise_if_wrong_class(task, 'predictions', list)
            for prediction in task.get('predictions', []):
                if not isinstance(prediction, dict):
                    logger.warning('Prediction must be dict, but "%s" found', str(type(prediction)))
                    continue

                ok = 'result' in prediction
                if not ok:
                    raise ValidationError('Prediction must have "result" fields')

            # task[meta]
            self.raise_if_wrong_class(task, 'meta', (dict, list))

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Add a 'result' key with a list value to every annotation dict, e.g. 'result': [] for a no-op annotation
  2. If the annotation has no labeling results, import it without the 'annotations' key entirely
  3. Validate the import JSON locally before POSTing: for each task, for each a in task.get('annotations', []): assert isinstance(a.get('result'), list)
  4. Remove pre-annotations/annotations from the payload if you only intend to import raw task data

Example fix

// before
{"data": {"text": "hi"}, "annotations": [{"completed_by": 1}]}
// after
{"data": {"text": "hi"}, "annotations": [{"completed_by": 1, "result": []}]}
Defensive patterns

Strategy: validation

Validate before calling

def validate_annotations(task):
    for a in task.get('annotations', []):
        if isinstance(a, dict) and 'result' not in a:
            raise ValueError('annotation missing result: %r' % a)
        if isinstance(a, dict) and not isinstance(a['result'], list):
            raise ValueError('annotation result must be a list')

Type guard

def has_valid_annotation(a):
    return isinstance(a, dict) and isinstance(a.get('result'), list)

Try / catch

try:
    client.import_tasks(id=project_id, tasks=tasks)
except LabelStudioError as e:
    if 'Annotation must have "result" fields' in str(e):
        tasks = [fix_annotation(t) for t in tasks]
        client.import_tasks(id=project_id, tasks=tasks)
    else:
        raise

Prevention

When it happens

Trigger: POSTing tasks to the task import API (e.g. /api/projects/<id>/import) with an item like {'data': {...}, 'annotations': [{'completed_by': 1}]} — an annotation dict present but missing 'result'. Only annotation entries that are dicts trigger this; non-dict entries are skipped with a warning.

Common situations: Scripts exporting annotations from other tools (or older Label Studio exports) that omit 'result'; hand-written import JSON where only annotations metadata (lead_time, completed_by) was copied; API clients building annotations programmatically and forgetting the results array.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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