Build1 distinct publisher3 min readPublished
Counting JSONTestSuite fixtures before writing any parser code turned up 19 must-reject number literals that Rust's stdlib conversions accept. That is 40% of the number cases, and the same gap opens wherever a stdlib conversion is looser than the wire format.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
Rust's float grammar is documented as `Sign? (inf | infinity | nan | Number)`, case insensitive [6]. `+Inf` and `-NaN` both parse, because Rust's own literal syntax admits them [9]. The integer grammar is `Sign? Digit+` with leading zeros permitted, and that is the entire explanation for `-01` and `-012` [7][8]. Neither name nor signature offers validation, and the author is careful to say this is not a bug in Rust: a conversion's contract is the host language's literal syntax, not the wire format's [12].
The other half of the failure has nothing to do with grammars. It is the token boundary. `1 000.0` sits in the corpus for exactly this reason, since a scanner that stops at the space and never asks what follows will accept `1` and then either choke or silently succeed [14]. The replacement is a byte state machine that enforces the RFC production, meaning optional minus, an integer part that is either `0` alone or a nonzero digit followed by digits, a fraction with at least one digit after the dot, an exponent with at least one digit after `e`, and then a terminator check [13].
The headline figure has two denominators. Nineteen accepted fixtures against 188 must-reject files is 10.1% [1]. Against the 47 number fixtures that are single well-formed literals, it is 40.4% [2]. The second number describes the decision you are actually making, because number syntax is all `from_str` was ever asked to judge, and it happens to reject the other 28 [7]. Number cases alone are 27.1% of the reject corpus [5].
For 10% to be your number, three things have to hold. Your parser must delegate number validation to the stdlib conversion. Your stdlib must be permissive in the same places Rust's is. And your input mix has to resemble a curated adversarial corpus. The third is the weak leg: 318 hand-named files are an editorial judgment about what is worth testing, not a traffic model [4]. If every document you read comes from one serializer you control, your observed rate is zero, and that tells you nothing about the week it stops being zero. What travels is the shape rather than the percentage, and the author says he checked whether the leniency was a quirk of one standard library and found it was not [16].
The measurement cost him the ordering discipline, not much code. The state machine is a few dozen lines, and he only knew they were the highest-value lines in the project because the fixtures were counted first [15]. The finished toolkit carries 4,432 lines of tests against 4,670 lines under `src/` [2], roughly 0.95 test lines per source line [4]. The empty `[dependencies]` table and a Cargo.lock holding exactly one package [1] make for a lockfile you can read out loud. On the query side it fields eleven builtins [17] and jq's exit codes, including 5 for input that is not JSON [18], which is the code that now means something specific.
Ranked by verification strength, evidence, and original report placement.
The project was built under the Zero Dependency hackathon rule of no third-party dependencies at all: the [dependencies] table in Cargo.toml is present and empty, and Cargo.lock holds exactly one package, the project itself. No serde, serde_json, clap, itoa or ryu.
jaq-lite is a hand-rolled RFC 8259 parser, a serializer and a jq-style query CLI with rustc-style caret diagnostics: 4,670 lines under src/ and 4,432 lines of tests, standard library only.
The author's finding: about 10% of the JSON documents that RFC 8259 says a parser must reject are accepted by Rust's own number parser.
JSONTestSuite's test_parsing/ directory holds 318 files named by what a parser should do with them: 95 y_ files must parse, 188 n_ files must be rejected, and 35 i_ files are left to the implementation. The author vendored it at a pinned commit before writing a line of parser code.
51 of the JSONTestSuite files are n_number_*. Four contain invalid UTF-8, leaving 47 that are a single well-formed literal, each tested against Rust's documented from_str grammars.
Rust's documented float from_str grammar is Sign? (inf | infinity | nan | Number), case insensitive.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · September 1, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
product
AI writes the Dockerfile, and the pipeline is still checking the app code1 distinct publisher
build
The repo's own control run deleted the 5-10x WASM claim from vizcrush's launch copy1 distinct publisher
build
The third answer: a dead-code tool allowed to say "not traced yet"1 distinct publisher
build
A retry cap is not a retry budget, and each language breaks it in a different place1 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.
One post, but it hands you the tools to check it
Everything rests on a single self-published account, and it is the rare one that makes itself falsifiable: a named corpus at a pinned commit, Rust's own documented grammars, nineteen fixtures listed individually, and arithmetic that lands where it should — 19 of 188 is the tenth, 19 of 47 is the 40%. What is missing is anyone re-running it. The line counts, the pinned commit and the claim that the corpus was vendored before the first line of parser code are the author's word, and no repository is cited in the piece we have.
The looseness is everywhere; the tool is a weekend old
Two different things are being taken up here and they pull in opposite directions. The permissive behaviour is live in software almost everyone runs — Python's json module documents accepting NaN and Infinity, and V8 funded a second grammar rather than reuse Number() — so the hazard has real deployment behind it. jaq-lite has none: it is a hackathon entry published with no users, downloads or dependents on record.
A corpus statistic dressed as a rate
The tenth is a fact about JSONTestSuite's composition, not about JSON in the wild: nineteen fixtures reach 10% of the must-reject set only because number cases are 27% of it. The dek's framing — 40% of the number literals — is the honest one. And 'Rust's own number parser' means f64::from_str, which the author himself says was never meant to parse JSON. The packaging inflates a little; the body deflates it again a few paragraphs later.
Hackathon entry with a headline to earn
A contest submission with a writeup attached pulls toward a round, quotable number and toward a weekend project sounding larger than it is. Pulling the other way: the finding is the author's own first instinct turning out wrong, the easy 'Rust has a bug' headline is explicitly refused, and the fixtures are named so a reader can disagree. Nothing is being sold, and no vendor or funder appears anywhere in this reporting.
Cheap to falsify, and nobody has bothered
Most of the confidence comes from how little trust the finding requires: a pinned public corpus and two documented grammars carry it, and both are open to any reader with a terminal. What holds the number down is thinner ground — one publisher, one author, no independent run of the harness. Usefully, the unverifiable parts are also the parts nothing depends on.