OtterMind/Chat2DB · error · RequestError

request body is not valid JSON

Error message

request body is not valid JSON

What it means

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.

Source

Thrown at script/github/qq_relay/relay_server.py:212

        expected = f"Bearer {self.relay_state.config.relay_token}"
        supplied = self.headers.get("Authorization", "")
        if not hmac.compare_digest(supplied, expected):
            raise RequestError(HTTPStatus.UNAUTHORIZED, "unauthorized")

    def _read_payload(self) -> Mapping[str, Any]:
        content_type = self.headers.get("Content-Type", "")
        if not content_type.lower().startswith("application/json"):
            raise RequestError(HTTPStatus.UNSUPPORTED_MEDIA_TYPE, "Content-Type must be JSON")
        try:
            content_length = int(self.headers.get("Content-Length", ""))
        except ValueError as error:
            raise RequestError(HTTPStatus.LENGTH_REQUIRED, "Content-Length is required") from error
        if content_length < 1 or content_length > MAX_REQUEST_BYTES:
            raise RequestError(HTTPStatus.REQUEST_ENTITY_TOO_LARGE, "request body is too large")
        try:
            payload = json.loads(self.rfile.read(content_length).decode("utf-8"))
        except (UnicodeDecodeError, json.JSONDecodeError) as error:
            raise RequestError(HTTPStatus.BAD_REQUEST, "request body is not valid JSON") from error
        if not isinstance(payload, Mapping):
            raise RequestError(HTTPStatus.BAD_REQUEST, "request body must be a JSON object")
        return payload

    def _validate_payload(self, payload: Mapping[str, Any]) -> tuple[str, str]:
        config = self.relay_state.config
        if payload.get("repository") != config.repository:
            raise RequestError(HTTPStatus.FORBIDDEN, "repository is not allowed")
        delivery_id = payload.get("delivery_id")
        if not isinstance(delivery_id, str) or not DELIVERY_ID_PATTERN.fullmatch(delivery_id):
            raise RequestError(HTTPStatus.BAD_REQUEST, "delivery_id is invalid")
        message = payload.get("message")
        if not isinstance(message, str) or not message.strip():
            raise RequestError(HTTPStatus.BAD_REQUEST, "message must be non-empty text")
        if len(message) > config.max_message_length:
            raise RequestError(HTTPStatus.BAD_REQUEST, "message is too long")
        if CONTROL_CHARACTER_PATTERN.search(message):
            raise RequestError(HTTPStatus.BAD_REQUEST, "message contains control characters")

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Send a well-formed JSON object encoded as UTF-8.
  2. Validate the body with a JSON parser before sending (e.g. python -m json.tool).
  3. Ensure Content-Length exactly matches the byte count of the body sent.
  4. Use a JSON serializer rather than string concatenation.

Example fix

python -c "import json,sys; json.load(open('body.json')); print('valid')"
Defensive patterns

Strategy: validation

Validate before calling

import json
body_text = body.decode("utf-8")  # raises UnicodeDecodeError if not UTF-8
json.loads(body_text)  # raises JSONDecodeError if malformed

Type guard

def is_valid_json_body(body: bytes) -> bool:
    try:
        json.loads(body.decode("utf-8"))
        return True
    except (UnicodeDecodeError, json.JSONDecodeError):
        return False

Try / catch

from urllib.error import HTTPError
try:
    response = send_relay_message(...)
except HTTPError as error:
    if error.code == 400:
        # validate/rewrite the JSON body, then retry once
        pass
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14). Data as JSON: /api/errors/1ccf2c83e9a6eff8. Report an issue: GitHub.