HumanSignal/label-studio · error · ValueError

annotation "result" can't be parse from str to JSON

Error message

annotation "result" can't be parse from str to JSON

What it means

AnnotationSerializer.validate_result() accepts the annotation 'result' either as parsed JSON (list) or as a JSON string, which it json.loads()s. If the string cannot be parsed as JSON, ValueError is raised (note: ValueError, not DRF ValidationError, so it surfaces as a 500 unless caught).

Source

Thrown at label_studio/tasks/serializers.py:198

        try:
            return super().create(*args, **kwargs)
        except IntegrityError as e:
            errors = [
                'UNIQUE constraint failed: task_completion.unique_id',
                'duplicate key value violates unique constraint "task_completion_unique_id_key"',
            ]
            if any([error in str(e) for error in errors]):
                raise AnnotationDuplicateError()
            raise

    def validate_result(self, value):
        data = value
        # convert from str to json if need
        if isinstance(value, str):
            try:
                data = json.loads(value)
            except:  # noqa: E722
                raise ValueError('annotation "result" can\'t be parse from str to JSON')

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

        # FIT-1669: collapse `(id, from_name, type)` collisions at the write boundary
        # so the annotation record never persists duplicate-id rows.
        return dedupe_annotation_result_list(data)

    def _resolve_project_for_validation(self, data):
        if 'task' in data:
            return data['task'].project
        if self.instance is not None:
            return self.instance.project
        task = self.context.get('task')
        if task is not None:
            return task.project
        return None

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Send result as an actual JSON array in the request body (correct Content-Type: application/json), not as a stringified blob
  2. If sending a string, ensure it is valid JSON: use json.dumps(obj) (double quotes, no trailing commas) — never str(obj) or repr(obj)
  3. Catch ValueError in custom code paths around serializer.is_valid() since this specific failure raises ValueError, not ValidationError
  4. Validate with json.loads(result) client-side before the request

Example fix

// before
payload = {"result": str(my_result_list)}          # "[{'id': 1, ...}]" -> invalid JSON
// after
payload = {"result": json.dumps(my_result_list)}   # or send the list itself
Defensive patterns

Strategy: validation

Validate before calling

import json

def assert_result_string_is_json(value):
    if isinstance(value, str):
        try:
            json.loads(value)
        except json.JSONDecodeError as e:
            raise ValueError(f"result string is not valid JSON: {e}")

Type guard

def is_parsable_json_string(value) -> bool:
    if not isinstance(value, str):
        return True
    try:
        json.loads(value)
        return True
    except json.JSONDecodeError:
        return False

Try / catch

try:
    ser = AnnotationSerializer(data=payload)
    ser.is_valid(raise_exception=True)
except ValueError as e:
    # validate_result raises ValueError (not ValidationError) on unparseable strings
    logger.error("annotation result is not valid JSON: %s", e)
except Exception as e:
    logger.error("annotation rejected: %s", e)

Prevention

When it happens

Trigger: POST/PUT of an annotation with result sent as a string that is not valid JSON — e.g. single-quoted JSON ({'a': 1}), Python repr of a dict, trailing commas, or a non-JSON string like "label-a" — to the annotations API or via AnnotationSerializer directly.

Common situations: Clients building the JSON string with Python str(dict) instead of json.dumps; form-data/text content types where the JSON string is preserved literally; copy-pasted results from logs; double-encoding mistakes.

Related errors


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