{"record":{"id":"9e7cae9f2fdf7479","repo":"apache/superset","slug":"json-not-valid","errorCode":null,"errorMessage":"JSON not valid","messagePattern":"JSON not valid","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"superset/annotation_layers/annotations/schemas.py","lineNumber":59,"sourceCode":"get_delete_ids_schema = {\n    \"type\": \"array\",\n    \"items\": {\"type\": \"integer\"},\n    \"example\": [1, 2, 3],\n}\n\nannotation_start_dttm = \"The annotation start date time\"\nannotation_end_dttm = \"The annotation end date time\"\nannotation_layer = \"The annotation layer id\"\nannotation_short_descr = \"A short description\"\nannotation_long_descr = \"A long description\"\nannotation_json_metadata = \"JSON metadata\"\n\n\ndef validate_json(value: Union[bytes, bytearray, str]) -> None:\n    try:\n        json.validate_json(value)\n    except json.JSONDecodeError as ex:\n        raise ValidationError(\"JSON not valid\") from ex\n\n\nclass AnnotationPostSchema(Schema):\n    short_descr = fields.String(\n        metadata={\"description\": annotation_short_descr},\n        required=True,\n        allow_none=False,\n        validate=[Length(1, 500)],\n    )\n    long_descr = fields.String(\n        metadata={\"description\": annotation_long_descr}, allow_none=True\n    )\n    start_dttm = fields.DateTime(\n        metadata={\"description\": annotation_start_dttm},\n        required=True,\n        allow_none=False,\n    )\n    end_dttm = fields.DateTime(","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/annotation_layers/annotations/schemas.py#L41-L77","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure json_metadata is a valid JSON document string: double-quoted keys and strings, no trailing commas, true/false/null literals.","In Python clients, pass json.dumps(metadata) — never str(metadata) or repr(metadata).","Validate the string with json.loads() locally before sending the request to fail fast with a better message.","If the field is optional for your use case, omit json_metadata entirely instead of sending an empty/garbage string."],"exampleFix":"# before\npayload = {\"short_descr\": \"note\", \"json_metadata\": str({\"color\": \"red\"})}\nclient.post(url, json=payload)  # {'color': 'red'} is not JSON\n\n# after\nimport json\npayload = {\"short_descr\": \"note\", \"json_metadata\": json.dumps({\"color\": \"red\"})}\nclient.post(url, json=payload)","handlingStrategy":"validation","validationCode":"import json\n\ndef json_metadata_field(metadata: dict | None) -> str | None:\n    if metadata is None:\n        return None\n    value = json.dumps(metadata)  # raises locally if not serializable\n    json.loads(value)             # round-trip proves it parses\n    return value","typeGuard":null,"tryCatchPattern":"except marshmallow.ValidationError as ex:\n    if \"JSON not valid\" in str(ex.messages):\n        # re-serialize metadata properly and retry the request\n        payload[\"json_metadata\"] = json.dumps(json.loads(payload_raw))","preventionTips":["Always json.dumps() metadata objects; never str()/repr().","Round-trip validate payloads containing JSON-in-string fields before sending."],"tags":["rest-api","annotations","json","marshmallow","validation"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}