Build1 distinct publisher3 min readPublished
NaN, a Python repr and a truncated download all fail without a position. The prefix binary search that recovers one is keyed to a V8-only phrase, so it quietly did nothing in Safari, and Node-based tests never saw it.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Sort the cases by whether the message carries an index and the split stops looking arbitrary. The located ones are positions where the grammar admits exactly one thing and the document supplied something else [3]. The unlocated ones are tokens JSON has no rule for anywhere, plus documents that simply stop [4], and there V8 quotes the document back at you and names the offending character instead [1]. That second set is what users generate: hand-edited config, a Python `repr` pasted into the wrong window, a download that got cut off [5].
The arithmetic on the fallback is worth doing in character reads. Twenty probes on a 260KB paste [7], each a full parse of a prefix, prefixes averaging about half the file, gives roughly 20 x 130,000 = 2.6 million characters read to produce one integer [1]. A single left-to-right pass reads 260,000, about a tenth of that [2]. Both numbers describe the post's document, and the multiplication only holds if the break sits somewhere near the middle. A stray `True` on line 1 is cheaper: the first probe still parses half the file, then every probe after it halves downward, and the series sums to roughly the document length [3]. The expensive case is a file that ends mid-value, because every prefix of it also fails with "unexpected end", so the search walks right and each probe parses nearly everything, up to 20 x 260,000 = 5.2 million reads [4]. The truncated download is both the case V8 refuses to locate and the worst case for locating it yourself.
Cross-engine, there is no version of the trick that works. According to the post, JavaScriptCore's wording is the better prose, "Single quotes are not allowed in JSON" against V8's "Expected property name or '}'", and it is useless for putting a marker in a gutter [13]. Ten documents run through the `jsc` helper that ships on macOS produced no position at all [11].
So the scanner. It is recursive descent whose return value is an offset: the first character that cannot legally appear where it does, `text.length` if the document ends mid-value, or `-1` if the whole thing parses [16]. It builds no AST and allocates nothing, moving a cursor and throwing when the cursor hits something impossible [17]. Throwing a number as the control-flow signal is the part that looks wrong and the reason it stays short, because any scan function can bail from arbitrary depth without threading a result type through the descent, and the catch treats `typeof offset === "number"` as a parse result and rethrows anything else as a real bug [18]. Since it returns an offset rather than a value, it is not a replacement for `JSON.parse`; it is what runs once `JSON.parse` has already thrown.
What you take on is a second copy of JSON's grammar next to the engine's: strings and escapes, numbers, the three literal words, the two containers [19]. That is a napkin's worth of surface [15], and the bet is that it changes less often than the messages do, which V8 rewrote in 2021 and again after [14]. If the marker in your gutter has to point at the right line for a truncated download in Safari, where the post reports `JSON.parse` never gives a position for anything [12], the scanner is the only implementation that does it.
Ranked by verification strength, evidence, and original report placement.
JSON.parse('{"a":NaN}') throws with the message: Unexpected token 'N', "{"a":NaN}" is not valid JSON. It names the offending character and quotes the document, but gives no position.
V8 has two JSON.parse error message shapes, and only one of them carries a character offset.
The V8 messages that include a position are the syntactic near-misses: trailing comma, single quote, unquoted key, bad escape, leading zero. V8 locates all five exactly.
The V8 messages without a position include NaN, undefined, Python's True, a stray comma in a nested object, and a truncated file.
The author's workaround binary searches the prefix: parse text.slice(0, mid); if it fails with 'unexpected end' you are still inside valid JSON, so go right; any other error means you have gone past the break, so go left.
Every probe is a full parse of a prefix, so the search costs O(n log n) character reads; on a 260KB document that is about 20 reparses.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · September 4, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
The repo's own control run deleted the 5-10x WASM claim from vizcrush's launch copy1 distinct publisher
build
A default that is not a guard: tinycolor2's palette functions never return on analogous(-1)1 distinct publisher
build
A Tauri plugin puts the backend in JavaScript, and moves where Rust becomes unavoidable1 distinct publisher
build
A receipt intent written before dispatch turns an SMS timeout into a poll instead of a resend1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Checkable in a console, sourced to one desk
Unusually for a single-source story, the load of it can be re-run by the reader: the NaN message is a console paste, and the jsc helper path is printed so JavaScriptCore's silence can be confirmed on any Mac. What cannot be re-run is the measurement — 78,360 mutation comparisons with zero mismatches and the eightfold timing are reported without a repository, harness or seed set — and the engine survey stops at V8 and JavaScriptCore, with Firefox untested.
One editor, the author's own
Deployment here means a single JSON editor: the string-matching search went out in it, was found to be a no-op in Safari, and was replaced by the scanner. This reporting covers that one editor alone, with the code shown as an excerpt to copy rather than something to install, a package, a repository or a download count.
Restrained fix, roomier framing
The technical claims are argued down rather than up: V8's offset wins wherever there is one, the scanner is called the fallback and not the replacement, and the recursion limit is conceded. The give is in the premise. "Exactly the failures users actually paste" rests on the author's experience, and the 60% figure he leans on falls out of his own random mutation run, which is a distribution of generated corruptions rather than of anything a user typed.
Practitioner writing up his own tool
The account publishing this is named for a formatter, and the scanner exists because the author wanted it under a keystroke handler in his editor. That gives him better access to the problem than a neutral observer would have, and it also means the two most favourable numbers in the piece — perfect agreement with V8 and an eightfold speedup — were produced and reported by the person whose tool they endorse, with nobody in the story positioned to check them.
Firm where reproducible, thin where measured
Confidence tracks reproducibility here almost exactly. The engine-behaviour findings are hard to get wrong and easy to check, so they hold; the Safari failure is a first-hand account of the author's own bug, which is credible and unaudited; the timing and agreement figures are single runs; and the claim about what real users paste has nothing behind it in this story at all.