Exactly one of

Checked 22 Sep 2026 · By Luke Czak

ArticleUnderstanding AIFree to read

JSON Schema’s oneOf means the data must validate against exactly one of the listed subschemas, not at least one, and the docs themselves note it costs more to check than anyOf because every subschema still has to be verified.

I picked oneOf the way you pick a word off a menu without reading past the first line: it sounded like the friendliest, most permissive of the three JSON Schema combining keywords, a stand-in for “any of these will do”. It is not, and the documentation on combining schemas says exactly why in a sentence I should have read properly on day one instead of skimming past it. I had used oneOf on every schema I wrote for months, on the assumption that all three keywords were basically flavours of the same idea.

allOf means the data has to be valid against every one of the listed subschemas at once. anyOf means valid against any of them, so at least one match is enough. oneOf means the data has to be valid against exactly one of the given subschemas, and that word, exactly, is doing all the real work I had been reading straight past for months.

I found this out because a payload I knew was correct started failing validation the day I added a second subschema to what had been a single-schema oneOf. Both subschemas described overlapping shapes of the same underlying object, so a perfectly valid payload matched both, and oneOf’s own definition made that a failure rather than a pass, because two matches is not exactly one. Switching the keyword to anyOf fixed the validation immediately, since anyOf was the rule I had actually wanted and had never actually chosen. It was an easy mistake to make and a five-minute one to fix, which is exactly the kind of mistake that survives longest in a codebase, because the cost of finding it is so much higher than the cost of correcting it once found.

What changed how I choose between the two afterwards is not really about correctness, it is about cost. The documentation notes that oneOf requires verification of every sub-schema, and flags that as something that can lead to increased processing times, because oneOf cannot declare a result until every subschema has been checked, to prove that exactly one matched rather than two. oneOf is not a stricter cousin of anyOf that costs the same to run. It is stricter and it is slower, every time, by the structure of what it has to prove.

None of this is subtle once it is written down, which is exactly why I had not caught it sooner. Three keywords that read as near-synonyms in casual conversation turn out to encode three different logical operators, with different failure behaviour and different runtime cost baked in, and the schema will not warn you which one you meant. It enforces whichever one you actually typed, correctly, every time, regardless of whether the choice behind it was deliberate or a guess dressed up as one. I read the combining-schemas reference in full after that, instead of pattern-matching the keyword that sounded closest to what I had in mind.

Once I understood the actual difference, I went back through every schema I had written that used oneOf and checked whether the subschemas inside it could ever both be true of the same payload. Most of them could not, by construction, so oneOf and anyOf would have behaved identically there and the choice had never actually mattered in practice. One schema besides the one that broke turned out to have the same overlap, sitting unnoticed because nothing had yet supplied a payload that satisfied both branches at once. Waiting for a coincidence to surface a modelling mistake is not a strategy, and rereading a specification’s own wording once is a lot cheaper than waiting for the second coincidence to find it for you.

Comments (0)

Sign in to comment.