discriminator rejects schemas whose tag values are Object.prototype member names (toString, constructor, ...)
(
.github/ISSUE_TEMPLATE/bug-or-error-report.md, labelbug report). Plain correctness bug,
prototype constructor. No matching reports found (re-checked 2026-07-20).
Title: discriminator rejects schemas whose tag values are Object.prototype member names (toString, constructor, ...)
What version of Ajv are you using? Does the issue happen if you use the latest version?
8.20.0, reproduced on HEAD f177fe32. The check is unchanged on master.
Ajv options object
const options = {discriminator: true}JSON Schema
Two distinct branches. The discriminator tag values are unique, so the schema is valid, yet
compilation throws because one value happens to be an Object.prototype member name:
{
"type": "object",
"discriminator": {"propertyName": "kind"},
"required": ["kind"],
"oneOf": [
{"properties": {"kind": {"const": "toString"}}, "required": ["a"]},
{"properties": {"kind": {"const": "foo"}}, "required": ["b"]}
]
}Sample data
{"kind": "toString", "a": 1}Your code
const Ajv = require("ajv")
const ajv = new Ajv(options)
const validate = ajv.compile(schema) // throws before any validation
console.log(validate(data), validate.errors)Validation result, data AFTER validation, error messages
Error: discriminator: "kind" values must be unique strings
at addMapping (.../lib/vocabularies/discriminator/index.ts:104)The schema never compiles. A scan over tag-value pairs:
[foo,bar] -> OK, validate({kind:'foo',a:1}) = true
[toString,foo] -> ERR: discriminator: "kind" values must be unique strings
[constructor,x] -> ERR: discriminator: "kind" values must be unique strings
[hasOwnProperty,y] -> ERR: discriminator: "kind" values must be unique strings
[valueOf,z] -> ERR: discriminator: "kind" values must be unique strings
[__proto__,w] -> ERR: discriminator: "kind" values must be unique strings
[dup,dup] -> ERR (correct: genuine duplicate)Ajv rejects each Object.prototype member name as a "duplicate" even when it occurs once
across all branches.
What results did you expect?
The schema compiles and validate({kind: "toString", a: 1}) returns true. "toString" is a
legal tag value and differs from "foo", so no duplicate exists. Genuine duplicates (two
branches both using const: "dog") must still throw, and they do; a fix has to preserve that.
Root cause
lib/vocabularies/discriminator/index.ts. The mapping accumulator is a plain object literal
(line 64) and the uniqueness guard uses the in operator (line 104):
const oneOfMapping: {[T in string]?: number} = {} // line 64
...
function addMapping(tagValue, i) {
if (typeof tagValue != "string" || tagValue in oneOfMapping) { // line 104
throw new Error(`discriminator: "${tagName}" values must be unique strings`)
}
oneOfMapping[tagValue] = i
}in walks the prototype chain: "toString" in {} is true for an empty map, as is
"constructor" in {}, "hasOwnProperty" in {}, "valueOf" in {} and "__proto__" in {}. The
first branch using such a value trips the guard against an inherited member.
Suggested fix
Make the accumulator prototype-free:
- const oneOfMapping: {[T in string]?: number} = {}
+ const oneOfMapping: {[T in string]?: number} = Object.create(null)or keep the literal and test own membership:
- if (typeof tagValue != "string" || tagValue in oneOfMapping) {
+ if (typeof tagValue != "string" || Object.prototype.hasOwnProperty.call(oneOfMapping, tagValue)) {Object.create(null) is the smaller change and also covers the assignment on line 107. A
regression test should compile a discriminator with const: "toString" / const: "constructor"
branches and confirm a real duplicate still throws.
Are you going to resolve the issue?
Yes. I can send the one-line Object.create(null) patch plus a test; flagging first in case you
prefer the hasOwnProperty form.
Found via property-based & differential bug-hunting, part of an effort to scale PBT (DepTyCheck-based) testing across the OSS ecosystem.
If this is intended / by-design: I'm really sorry, please just close it — no need to flag or ban me. I'm trying to scale property-based testing across the whole ecosystem and my publishing agents may have gotten this one wrong. I read every issue and follow up on each.
Source: ajv-validator/ajv