infiniflow/ragflow · warning · TypeError

JSON payload must be an object.

Error message

JSON payload must be an object.

What it means

Raised by _coerce_request_data when a JSON body parses successfully but is not an object — arrays ([1,2]), numbers, booleans, or null. Handlers use .get() on the payload, so non-object JSON is rejected with TypeError at the parsing layer.

Source

Thrown at api/utils/api_utils.py:81

    if hasattr(request, "_cached_payload"):
        return request._cached_payload
    payload: Any = None

    body_bytes = await request.get_data()
    has_body = bool(body_bytes)
    content_type = (request.content_type or "").lower()
    is_json = content_type.startswith("application/json")

    if not has_body:
        payload = {}
    elif is_json:
        payload = await request.get_json(force=False, silent=False)
        if isinstance(payload, dict):
            payload = payload or {}
        elif isinstance(payload, str):
            raise AttributeError("'str' object has no attribute 'get'")
        else:
            raise TypeError("JSON payload must be an object.")
    else:
        form = await request.form
        payload = form.to_dict() if form else None
        if payload is None:
            raise TypeError("Request body is not a valid form payload.")

    request._cached_payload = payload
    return payload


async def get_request_json():
    return await _coerce_request_data()


def serialize_for_json(obj):
    """
    Recursively serialize objects to make them JSON serializable.
    Handles ModelMetaclass and other non-serializable objects.

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Wrap list payloads in an object: send {"ids": [...]} instead of [...].
  2. Check the endpoint's documented request schema and match the top-level type.
  3. Add a client-side assertion that the serialized body starts with '{' for JSON endpoints.

Example fix

# before
requests.post(url, json=['doc1', 'doc2'])  # array -> TypeError

# after
requests.post(url, json={'ids': ['doc1', 'doc2']})
Defensive patterns

Strategy: type-guard

Validate before calling

assert isinstance(payload, dict), 'top-level JSON must be an object'

Type guard

def is_json_object(payload) -> bool:
    return isinstance(payload, dict)

Try / catch

try:
    data = await get_request_json()
except TypeError:
    return json_error_response('JSON payload must be an object', 400)

Prevention

When it happens

Trigger: POSTing a JSON array as the request body ('["a","b"]'), a bare number/true/null body, or an API client whose serializer emits a list where the endpoint expects an object like {"ids": [...]}.

Common situations: Clients wrapping batch payloads as bare arrays when the endpoint wants an object; copy-pasted example bodies from other APIs; null bodies sent explicitly.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/f0d199c063add28a. Report an issue: GitHub.