apache/superset · error · ValidationError

JSON not valid

Error message

JSON not valid

What it means

A marshmallow ValidationError raised by validate_json() in the annotation layer schemas: it attempts json.loads (via superset.utils.json.validate_json) on the json_metadata field of an annotation POST/PUT payload, and when the value is not parseable JSON it converts the JSONDecodeError into 'JSON not valid'. This is field-level input validation on the annotation REST API.

Source

Thrown at superset/annotation_layers/annotations/schemas.py:59

get_delete_ids_schema = {
    "type": "array",
    "items": {"type": "integer"},
    "example": [1, 2, 3],
}

annotation_start_dttm = "The annotation start date time"
annotation_end_dttm = "The annotation end date time"
annotation_layer = "The annotation layer id"
annotation_short_descr = "A short description"
annotation_long_descr = "A long description"
annotation_json_metadata = "JSON metadata"


def validate_json(value: Union[bytes, bytearray, str]) -> None:
    try:
        json.validate_json(value)
    except json.JSONDecodeError as ex:
        raise ValidationError("JSON not valid") from ex


class AnnotationPostSchema(Schema):
    short_descr = fields.String(
        metadata={"description": annotation_short_descr},
        required=True,
        allow_none=False,
        validate=[Length(1, 500)],
    )
    long_descr = fields.String(
        metadata={"description": annotation_long_descr}, allow_none=True
    )
    start_dttm = fields.DateTime(
        metadata={"description": annotation_start_dttm},
        required=True,
        allow_none=False,
    )
    end_dttm = fields.DateTime(

View on GitHub (pinned to f4587218dd)

Solutions

  1. Ensure json_metadata is a valid JSON document string: double-quoted keys and strings, no trailing commas, true/false/null literals.
  2. In Python clients, pass json.dumps(metadata) — never str(metadata) or repr(metadata).
  3. Validate the string with json.loads() locally before sending the request to fail fast with a better message.
  4. If the field is optional for your use case, omit json_metadata entirely instead of sending an empty/garbage string.

Example fix

# before
payload = {"short_descr": "note", "json_metadata": str({"color": "red"})}
client.post(url, json=payload)  # {'color': 'red'} is not JSON

# after
import json
payload = {"short_descr": "note", "json_metadata": json.dumps({"color": "red"})}
client.post(url, json=payload)
Defensive patterns

Strategy: validation

Validate before calling

import json

def json_metadata_field(metadata: dict | None) -> str | None:
    if metadata is None:
        return None
    value = json.dumps(metadata)  # raises locally if not serializable
    json.loads(value)             # round-trip proves it parses
    return value

Try / catch

except marshmallow.ValidationError as ex:
    if "JSON not valid" in str(ex.messages):
        # re-serialize metadata properly and retry the request
        payload["json_metadata"] = json.dumps(json.loads(payload_raw))

Prevention

When it happens

Trigger: POST or PUT to /api/v1/annotation_layer/{layer_id}/annotation/ with json_metadata supplied as a malformed JSON string — unbalanced braces, single quotes instead of double quotes, Python-style None/True literals, trailing commas, or a truncated string. Also passing raw bytes that decode to invalid JSON.

Common situations: Sending dict objects that get str()'d by a client instead of json.dumps()'d; hand-crafted curl payloads with quoting issues where the shell mangles quotes; copying JSON from a Python REPL (single quotes) into a request body; LLM/script-generated payloads with trailing commas.

Related errors


AI-assisted analysis of apache/superset@f4587218dd (2026-08-14). Data as JSON: /api/errors/9e7cae9f2fdf7479. Report an issue: GitHub.