jsonify serialises float("nan") and float("inf") as the bare literals NaN and Infinity. Those are not valid JSON (RFC 8259 allows only decimal numbers), so the response is served as application/json but cannot be parsed by a strict parser, including the browser's JSON.parse.
This inherits from json.dumps, whose allow_nan defaults to True. DefaultJSONProvider sets default, ensure_ascii and sort_keys, but never touches allow_nan, so the permissive default applies.
The path to it is ordinary: any average, ratio or division that can meet an empty input produces nan without anything looking wrong on the server.
Replication:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/stats")
def stats():
values = []
mean = sum(values) / len(values) if values else float("nan")
return jsonify({"count": len(values), "mean": mean})
with app.test_client() as c:
r = c.get("/stats")
print(r.headers["Content-Type"]) # application/json
print(r.get_data(as_text=True)) # {"count":0,"mean":NaN}
The response advertises application/json with a 200, but the body is not JSON:
import json
json.loads('{"count":0,"mean":NaN}') # accepted, Python is permissive by default
json.loads('{"count":0,"mean":NaN}',
parse_constant=lambda x: (_ for _ in ()).throw(ValueError(x)))
# ValueError: NaN <- what a strict parser, and JSON.parse in a browser, does
So the failure surfaces in the client rather than the server, which makes it awkward to trace back.
Expected: either the same behaviour with it documented, or serialisation raises so the problem shows up server-side, where ValueError: Out of range float values are not JSON compliant: nan names the cause immediately.
There is currently no class attribute for this, although the plumbing already works, since dumps forwards **kwargs to json.dumps:
app.json.dumps({"a": float("nan")}, allow_nan=False)
# ValueError: Out of range float values are not JSON compliant: nan
DefaultJSONProvider exposes sort_keys, ensure_ascii and compact as class attributes, so an allow_nan attribute alongside them would fit the existing pattern and let applications choose, without changing the default for anyone. I could not find NaN mentioned in the DefaultJSONProvider or flask.json.dumps docstrings, so at minimum a documentation note seems worthwhile.
Happy to send a PR for whichever you prefer, an allow_nan provider attribute or just the docs.
Environment:
- Python 3.14
- Flask 3.1.3
- Werkzeug 3.1.8
jsonifyserialisesfloat("nan")andfloat("inf")as the bare literalsNaNandInfinity. Those are not valid JSON (RFC 8259 allows only decimal numbers), so the response is served asapplication/jsonbut cannot be parsed by a strict parser, including the browser'sJSON.parse.This inherits from
json.dumps, whoseallow_nandefaults toTrue.DefaultJSONProvidersetsdefault,ensure_asciiandsort_keys, but never touchesallow_nan, so the permissive default applies.The path to it is ordinary: any average, ratio or division that can meet an empty input produces
nanwithout anything looking wrong on the server.Replication:
The response advertises
application/jsonwith a 200, but the body is not JSON:So the failure surfaces in the client rather than the server, which makes it awkward to trace back.
Expected: either the same behaviour with it documented, or serialisation raises so the problem shows up server-side, where
ValueError: Out of range float values are not JSON compliant: nannames the cause immediately.There is currently no class attribute for this, although the plumbing already works, since
dumpsforwards**kwargstojson.dumps:DefaultJSONProviderexposessort_keys,ensure_asciiandcompactas class attributes, so anallow_nanattribute alongside them would fit the existing pattern and let applications choose, without changing the default for anyone. I could not findNaNmentioned in theDefaultJSONProviderorflask.json.dumpsdocstrings, so at minimum a documentation note seems worthwhile.Happy to send a PR for whichever you prefer, an
allow_nanprovider attribute or just the docs.Environment: