psf/requests · error · InvalidJSONError

{ve}

Error message

{ve}

What it means

Raised by PreparedRequest.prepare_body when json.dumps fails on the supplied json= argument. The default dumps is called with allow_nan=False, so NaN/Infinity (which are invalid per strict JSON) always trigger this; any other ValueError from the encoder (circular references via a custom encoder, non-serializable types) is also wrapped as InvalidJSONError. This signals that the request body could not be produced.

Source

Thrown at src/requests/models.py:596

    ) -> None:
        """Prepares the given HTTP body data."""

        # Check if file, fo, generator, iterator.
        # If not, run through normal process.

        # Nottin' on you.
        body = None
        content_type = None

        if not data and json is not None:
            # urllib3 requires a bytes-like body. Python 2's json.dumps
            # provides this natively, but Python 3 gives a Unicode string.
            content_type = "application/json"

            try:
                body = complexjson.dumps(json, allow_nan=False)
            except ValueError as ve:
                raise InvalidJSONError(ve, request=self)

            if not isinstance(body, bytes):
                body = body.encode("utf-8")

        # data that proxies attributes to underlying objects needs hasattr
        is_iterable = isinstance(data, Iterable) or hasattr(data, "__iter__")
        if is_iterable and not isinstance(data, (str, bytes, list, tuple, Mapping)):
            try:
                length = super_len(data)
            except (TypeError, AttributeError, UnsupportedOperation):
                length = None

            body = data

            if getattr(body, "tell", None) is not None:
                # Record the current file position before reading.
                # This will allow us to rewind a file in the event
                # of a redirect.

View on GitHub (pinned to 8068356288)

Solutions

  1. Sanitize NaN/Inf values before passing json= (replace with None or a string), or post-process with a custom serializer.
  2. For non-serializable types, convert to plain dicts/lists first, or use a json= argument that is already a str produced by your own dumps with a default hook.
  3. Catch InvalidJSONError and either serialize manually or fail the request gracefully.

Example fix

// before
import math
payload = {'ratio': float('nan')}
requests.post(url, json=payload)

// after
import math
payload = {'ratio': None if math.isnan(r) else r for r in [...]}
requests.post(url, json=payload)
Defensive patterns

Strategy: validation

Validate before calling

import json, math, requests

def json_safe(obj):
    if isinstance(obj, float) and (math.isnan(obj) or math.isinf(obj)):
        return None
    if isinstance(obj, dict):
        return {k: json_safe(v) for k, v in obj.items()}
    if isinstance(obj, (list, tuple)):
        return [json_safe(v) for v in obj]
    return obj

Type guard

import math

def is_json_safe(obj) -> bool:
    if isinstance(obj, float):
        return not (math.isnan(obj) or math.isinf(obj))
    if isinstance(obj, dict):
        return all(is_json_safe(v) for v in obj.values())
    if isinstance(obj, (list, tuple)):
        return all(is_json_safe(v) for v in obj)
    return obj is None or isinstance(obj, (str, int, bool))

Try / catch

from requests.exceptions import InvalidJSONError

try:
    resp = requests.post(url, json=payload)
except InvalidJSONError:
    # sanitize and retry, or serialize manually
    resp = requests.post(url, data=json.dumps(payload, default=str))

Prevention

When it happens

Trigger: Passing json=float('nan') or json=float('inf') (rejected by allow_nan=False); passing a non-serializable object (datetime, set, custom class) without a default= hook; circular references in nested structures.

Common situations: Logging/metrics payloads containing NaN from numeric processing; ORM objects or dataclasses not registered with a JSON encoder; third-party objects that look dict-like but aren't serializable.

Related errors


AI-assisted analysis of psf/requests@8068356288 (2026-08-11). Data as JSON: /api/errors/21533145d34fbe17. Report an issue: GitHub.