Package
@feathersjs/mongodb — specifically src/converters.ts, the keywordObjectId export.
Description
The keywordObjectId AJV keyword throws an Error when the value is not a valid ObjectId string:
return function (value: string, obj: any) {
const { parentData, parentDataProperty } = obj
try {
parentData[parentDataProperty] = new ObjectId(value)
return true
} catch (error) {
throw new Error(`invalid objectid for property "${parentDataProperty}"`)
}
}
This breaks AJV's anyOf / oneOf evaluation. When a schema uses a union type like:
{
"anyOf": [
{ "type": "string", "objectid": true },
{ "type": "null" }
]
}
AJV is supposed to try each branch and succeed if any passes. But because the keyword throws instead of returning false, AJV cannot gracefully fall through to the { "type": "null" } branch — the exception propagates up the call stack.
Steps to reproduce
- Register
keywordObjectId on an AJV instance with coerceTypes: true and useDefaults: true
- Define a schema with a nullable objectid field:
const schema = Type.Object({
refId: Type.Union([Type.String({ objectid: true }), Type.Null()], { default: null }),
})
- Validate an object where
refId is missing (so the default null is applied)
- Expected: Validation passes —
null matches the Type.Null() branch
- Actual: Throws
Error: invalid objectid for property "refId"
Root cause
With coerceTypes: true, AJV attempts to coerce null into a string when evaluating the first anyOf branch. The coerced value (empty string) reaches the objectid keyword, which calls new ObjectId(""), catches the error, and re-throws instead of returning false.
AJV keyword validators must return false (not throw) to signal failure, otherwise AJV cannot continue evaluating alternative branches.
Suggested fix
return function (value: string, obj: any) {
const { parentData, parentDataProperty } = obj
try {
parentData[parentDataProperty] = new ObjectId(value)
return true
} catch (error) {
- throw new Error(`invalid objectid for property "${parentDataProperty}"`)
+ return false
}
}
This allows AJV to produce a standard validation error (referencing the keyword) rather than an uncaught exception, and correctly supports anyOf / oneOf unions with nullable objectid fields.
Package
@feathersjs/mongodb— specificallysrc/converters.ts, thekeywordObjectIdexport.Description
The
keywordObjectIdAJV keyword throws anErrorwhen the value is not a valid ObjectId string:This breaks AJV's
anyOf/oneOfevaluation. When a schema uses a union type like:{ "anyOf": [ { "type": "string", "objectid": true }, { "type": "null" } ] }AJV is supposed to try each branch and succeed if any passes. But because the keyword throws instead of returning
false, AJV cannot gracefully fall through to the{ "type": "null" }branch — the exception propagates up the call stack.Steps to reproduce
keywordObjectIdon an AJV instance withcoerceTypes: trueanduseDefaults: truerefIdis missing (so the defaultnullis applied)nullmatches theType.Null()branchError: invalid objectid for property "refId"Root cause
With
coerceTypes: true, AJV attempts to coercenullinto a string when evaluating the firstanyOfbranch. The coerced value (empty string) reaches theobjectidkeyword, which callsnew ObjectId(""), catches the error, and re-throws instead of returningfalse.AJV keyword validators must return
false(not throw) to signal failure, otherwise AJV cannot continue evaluating alternative branches.Suggested fix
return function (value: string, obj: any) { const { parentData, parentDataProperty } = obj try { parentData[parentDataProperty] = new ObjectId(value) return true } catch (error) { - throw new Error(`invalid objectid for property "${parentDataProperty}"`) + return false } }This allows AJV to produce a standard validation error (referencing the keyword) rather than an uncaught exception, and correctly supports
anyOf/oneOfunions with nullable objectid fields.