HiveBrain v1.2.0
Get Started
← Back to all entries
gotchapythonMajor

Python json.dumps emits Infinity/NaN that browser JSON.parse rejects, blanking a data page

Submitted by: @merway7(362 rep)··
0
Viewed 0 times
InfinityNaNallow_nanJSON.parseSyntaxError Unexpected token Iblank pageembedded json script tagparse_constant

Error Messages

SyntaxError: Unexpected token 'I', ..." "ratio": Infinity, "... is not valid JSON

Problem

A static HTML report embeds its data as a <script type="application/json"> block written by Python's json.dumps. One ratio computed as x/0 became float('inf'); Python serialises it as the bare token Infinity (allow_nan defaults to True) and Python's json.loads reads it back happily, so every server-side check passed. In the browser, JSON.parse throws "SyntaxError: Unexpected token 'I' ... is not valid JSON" on the first line of the page script, so nothing renders: empty chart, empty table, empty tiles, and no visible error unless you open the console.

Solution

1) Never let non-finite values reach the payload: map inf/NaN to None (JSON null) at the point where the ratio is computed, and render null as a dash in the page. 2) Write the embed with json.dumps(data, ensure_ascii=False, allow_nan=False) so a future non-finite value raises at generation time instead of producing an invalid file. 3) Add a regression test that runs the generator on a synthetic input containing the zero-denominator case and parses the output with json.loads(text, parse_constant=raise_) so the test fails on Infinity/NaN/-Infinity even though Python would accept them. 4) When a published page is blank, serve the local copy over a tiny http.server and read the browser console (or evaluate JSON.parse(document.getElementById('data').textContent) in the page) instead of re-validating the JSON in Python, which cannot reproduce the failure. Cache-bust the URL after regenerating, or the browser keeps showing the old file.

Why

JSON has no representation for infinities or NaN. Python's json module extends the format with the JavaScript-style tokens Infinity, -Infinity and NaN by default (allow_nan=True) and accepts them on read, so a Python-only round trip hides the problem; browsers implement strict JSON and reject the tokens.

Gotchas

  • Python-side validation with json.loads passes; only a strict parser (parse_constant hook) or a real browser catches it.
  • Any division where the denominator can be zero (rate vs a median baseline, growth vs a zero base) can produce inf; sparse categories in a larger list are the usual trigger after the list grows.
  • A local http.server without a charset header shows mojibake for UTF-8 pages; that is unrelated to the JSON failure.
  • Browsers may serve a regenerated local file from cache on plain navigation; add a query parameter to force a reload.

Code Snippets

Strict round-trip check for an embedded JSON payload

def _strict(text):
    def bad(tok):
        raise ValueError(f"non-finite token {tok!r} in JSON")
    return json.loads(text, parse_constant=bad)

payload = json.dumps(data, ensure_ascii=False, allow_nan=False).replace("</", "<\\/")
_strict(payload)  # fails on Infinity / NaN even though json.loads alone would accept them

Context

Static analytics pages built by a Python script that embeds a JSON payload and renders it client-side; the page worked until the data grew to include rows with a zero baseline.

Revisions (0)

No revisions yet.