JSON Toolkit

Can NaN or Infinity appear in JSON

Bottom line: JSON has only decimal number literals. JSON.stringify turns NaN and Infinity into null without warning, which is why "the number field is sometimes null" keeps showing up in production.

Beginner~3 min readPublished 2026-10-30Updated 2026-10-30
What actually happens to NaN and Infinity in JSON

Bottom line: JSON cannot represent NaN or Infinity — JSON.stringify silently turns both into null — so encode the state explicitly with null plus a reason field, or a documented string value.

JSON.stringify({ ratio: NaN, limit: Infinity });
// '{"ratio":null,"limit":null}'

No error, no warning. Two numbers become null.

What the grammar allows

number = [ minus ] int [ frac ] [ exp ]

Decimals only. NaN and Infinity are IEEE 754 sentinels, not literals. Written into JSON they are invalid identifiers, and since their comparison semantics differ across languages, the spec excludes them rather than picking a representation.

What each language does instead

Language Behaviour
JavaScript JSON.stringify Silently emits null
Python json.dumps Emits NaN by default (non-standard); raises with allow_nan=False
Go encoding/json Returns unsupported value: NaN
Java Jackson Emits the string "NaN" by default

Four behaviours for one value. When a cross-language API shows a number field that is intermittently null, check this layer first.

What to do instead

Documented strings. "NaN", "Infinity" with a type note. Costs every consumer an explicit check.

null plus a status field. Better: {"avgLatency": null, "avgLatencyStatus": "no-sample"}. The reason travels with the value.

Rethink the unit. Most Infinity results come from dividing by zero samples. Returning null and rendering "no data yet" usually matches the business truth better than an infinite number.

Related traps

  • Chart libraries break the line on null — confirm that is what you want
  • JSON.stringify also drops undefined, functions and symbols entirely; those keys vanish rather than becoming null
  • Python's default allow_nan=True produces output that JSON.parse rejects

Further reading