What the spec actually says
From RFC 8259 §4:
"The names within an object SHOULD be unique. … When the names within an object are not unique, the behavior of software that receives such an object is unpredictable."
"SHOULD" not "MUST". The spec explicitly allows non-unique keys and explicitly refuses to define what to do with them. That decision was punted to implementations, and they disagree.
What parsers actually do
| Language / library | Default behavior | Strict mode? |
|---|---|---|
JavaScript (JSON.parse) | Last wins | No |
Python (json) | Last wins | Via object_pairs_hook |
Go (encoding/json) | Last wins | No |
Rust (serde_json) | Last wins (default) | preserve_order + manual check |
| Java (Jackson) | Last wins (default) | FAIL_ON_READING_DUP_TREE_KEY |
| Java (Gson) | Last wins | No built-in |
Ruby (json) | Last wins | No |
PHP (json_decode) | Last wins | No |
Almost universal "last wins". The exceptions are strict-mode flags (Jackson) and parsers that surface raw key/value pairs so you can decide for yourself.
How duplicates sneak in
Hand-edited config
Someone copy-pastes a section, edits one half, and forgets to
delete the other. Common in package.json,
tsconfig.json, composer.json.
{
"scripts": {
"build": "tsc",
"test": "jest",
"build": "tsc -w" ← duplicate, last wins
}
} Programmatic merging without dedupe
A pipeline that concatenates JSON arrays of objects, then re-keys them by id without checking for collisions, will produce duplicates if two upstream sources have the same id.
Case-sensitivity surprises
JSON is case-sensitive: id and ID are
different keys, not duplicates. Parsers happily keep both.
Schema validators and ORMs often case-fold downstream and
suddenly the duplicate appears at a layer the JSON parser
already cleared.
Detecting duplicates in code
JSON.parse won't help — by the time you have an
object, duplicates are already collapsed. You need to inspect
raw pairs.
Python: object_pairs_hook
import json
def reject_dupes(pairs):
seen = {}
for k, v in pairs:
if k in seen:
raise ValueError(f"Duplicate key: {k}")
seen[k] = v
return seen
json.loads(text, object_pairs_hook=reject_dupes) JavaScript: token scan
Use a streaming parser (stream-json) or walk the
text yourself with a small tokenizer. The reviver passed to
JSON.parse runs after deduplication, so it
can't help.
Java: Jackson strict mode
ObjectMapper m = new ObjectMapper()
.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY); This throws on the first duplicate. Off by default for backwards-compatibility.
The defensive rule
- Validate at ingest. Any JSON crossing a system boundary (HTTP request, file load, queue message) should pass through a duplicate-key check before deeper processing.
- Don't rely on "last wins". Even if your language does it today, a future version, a different serializer, or a CDN's JSON minifier might reorder keys.
- Use a schema (JSON Schema, Zod) that fails loudly on unexpected shapes. A duplicated key often produces a value of the wrong type, which a typed schema catches even when the parser doesn't.
Related
FAQ
Are duplicate keys legal JSON?
Technically yes. RFC 8259 §4 says names within an object SHOULD be unique, but does not require it. Behavior with duplicates is left to the implementation, which is why the same input parses differently in different languages.
Which parser keeps which value?
JavaScript's JSON.parse, Python's json (default), Go's encoding/json, and most major libraries keep the LAST occurrence — later keys overwrite earlier ones. Some libraries take the first or merge. Never rely on it; deduplicate at the source.
How do I detect duplicates if the parser silently drops them?
Pass an object_pairs_hook (Python), a reviver (JS), or use a streaming parser to inspect raw key occurrences before they're collapsed into an object. Our linter uses a token scan that flags every duplicate with line + column.
Is JSON Lines / NDJSON affected?
Each line is its own JSON value, so duplicates within a line are caught per-line. But multiple lines with the same 'id' are not duplicate keys — they're separate records. Different problem, different fix.
What about JSON Schema's uniqueItems?
uniqueItems applies to array items, not object keys. There is no JSON Schema keyword that forbids duplicate keys directly — you have to validate before parsing or use a strict parser that throws on them.