{"record":{"id":"1ccf2c83e9a6eff8","repo":"OtterMind/Chat2DB","slug":"request-body-is-not-valid-json","errorCode":null,"errorMessage":"request body is not valid JSON","messagePattern":"request body is not valid JSON","errorType":"http","errorClass":"RequestError","httpStatus":400,"severity":"error","filePath":"script/github/qq_relay/relay_server.py","lineNumber":212,"sourceCode":"        expected = f\"Bearer {self.relay_state.config.relay_token}\"\n        supplied = self.headers.get(\"Authorization\", \"\")\n        if not hmac.compare_digest(supplied, expected):\n            raise RequestError(HTTPStatus.UNAUTHORIZED, \"unauthorized\")\n\n    def _read_payload(self) -> Mapping[str, Any]:\n        content_type = self.headers.get(\"Content-Type\", \"\")\n        if not content_type.lower().startswith(\"application/json\"):\n            raise RequestError(HTTPStatus.UNSUPPORTED_MEDIA_TYPE, \"Content-Type must be JSON\")\n        try:\n            content_length = int(self.headers.get(\"Content-Length\", \"\"))\n        except ValueError as error:\n            raise RequestError(HTTPStatus.LENGTH_REQUIRED, \"Content-Length is required\") from error\n        if content_length < 1 or content_length > MAX_REQUEST_BYTES:\n            raise RequestError(HTTPStatus.REQUEST_ENTITY_TOO_LARGE, \"request body is too large\")\n        try:\n            payload = json.loads(self.rfile.read(content_length).decode(\"utf-8\"))\n        except (UnicodeDecodeError, json.JSONDecodeError) as error:\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"request body is not valid JSON\") from error\n        if not isinstance(payload, Mapping):\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"request body must be a JSON object\")\n        return payload\n\n    def _validate_payload(self, payload: Mapping[str, Any]) -> tuple[str, str]:\n        config = self.relay_state.config\n        if payload.get(\"repository\") != config.repository:\n            raise RequestError(HTTPStatus.FORBIDDEN, \"repository is not allowed\")\n        delivery_id = payload.get(\"delivery_id\")\n        if not isinstance(delivery_id, str) or not DELIVERY_ID_PATTERN.fullmatch(delivery_id):\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"delivery_id is invalid\")\n        message = payload.get(\"message\")\n        if not isinstance(message, str) or not message.strip():\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"message must be non-empty text\")\n        if len(message) > config.max_message_length:\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"message is too long\")\n        if CONTROL_CHARACTER_PATTERN.search(message):\n            raise RequestError(HTTPStatus.BAD_REQUEST, \"message contains control characters\")","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/script/github/qq_relay/relay_server.py#L194-L230","documentation":"Raised by RelayHandler._read_payload (relay_server.py:212) as a RequestError(HTTP 400) when the request body cannot be decoded as UTF-8 or parsed as JSON. This is checked after Content-Type and Content-Length pass, so it specifically signals malformed body bytes.","triggerScenarios":"A POST whose body is not valid JSON (truncated, syntax error) or not valid UTF-8 (binary/latin-1 bytes). UnicodeDecodeError or json.JSONDecodeError during json.loads triggers it.","commonSituations":"A truncated body (Content-Length mismatch); hand-crafted JSON with a trailing comma or single quotes; binary upload; encoding mismatch; a proxy corrupting the body; a client streaming incomplete JSON.","solutions":["Send a well-formed JSON object encoded as UTF-8.","Validate the body with a JSON parser before sending (e.g. python -m json.tool).","Ensure Content-Length exactly matches the byte count of the body sent.","Use a JSON serializer rather than string concatenation."],"exampleFix":"python -c \"import json,sys; json.load(open('body.json')); print('valid')\"","handlingStrategy":"validation","validationCode":"import json\nbody_text = body.decode(\"utf-8\")  # raises UnicodeDecodeError if not UTF-8\njson.loads(body_text)  # raises JSONDecodeError if malformed\n","typeGuard":"def is_valid_json_body(body: bytes) -> bool:\n    try:\n        json.loads(body.decode(\"utf-8\"))\n        return True\n    except (UnicodeDecodeError, json.JSONDecodeError):\n        return False","tryCatchPattern":"from urllib.error import HTTPError\ntry:\n    response = send_relay_message(...)\nexcept HTTPError as error:\n    if error.code == 400:\n        # validate/rewrite the JSON body, then retry once\n        pass\n    raise","preventionTips":["Serialize payloads with json.dumps, not string concatenation.","Ensure Content-Length matches the exact UTF-8 byte count.","Validate with a JSON parser before sending."],"tags":["http","relay","validation","json"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}