Без additionalProperties перевірка схеми нічого не перевіряє
Головне: Успішна перевірка не означає чисті дані. Pydantic типово ігнорує невідомі поля, Zod їх вилучає, Ajv пропускає — вигадані параметри й одруківки проходять тихо. Стаття показує три форми мовчазного прийняття та дає повні рецепти: закривати об'єкти, контролювати приведення типів, підтягувати кожен рівень вкладеності.

Висновок: типово валідатори ігнорують незаявлені поля, а не відхиляють їх. Без additionalProperties: false успішна перевірка лише підтверджує, що заявлені поля в порядку.
Найнебезпечніша поведінка валідатора — не помилка, а тиша. Одруківка в ключі або вигаданий моделью параметр проходить крізь валідацію беззвучно й вибухає у бізнес-логіці.
Один: перевірка, що «пройшла»
{
"type": "object",
"properties": {
"order_id": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["order_id", "amount"]
}
Передайте {"order_id": "A1", "amount": 50, "reasn_code": "damaged"} — третій ключ є одруківкою. Ajv з типовими налаштуваннями дає прохід. Зайве поле ніде не з'являється у помилках.
У специфікації JSON Schema значення additionalProperties типово true: незаявлені поля дозволені. Ви заявили «які поля є», але не «тільки ці поля можуть бути».
Два: три варіанти мовчання
| Середовище | Типова поведінка | Наслідок |
|---|---|---|
| Ajv (JS) | пропускає | брудні дані потрапляють у логіку |
Zod z.object |
вилучає | поле зникає, об'єкт коротшає |
| Pydantic v2 | ігнорує (extra="ignore") |
те саме, без сліду в журналах |
Вилучення прихованіше за пропуск. Обидві бібліотеки з коробки мовчки провалюються однаково.
Три: виклики інструментів LLM загострюють проблему
Аргументи інструментів генерує модель: вона впевнено вигадує назви полів і передає "3" замість числа. Платформи визначають параметри через JSON Schema, але типово не змушують схему бути закритою — strict-режим треба вмикати явно, а граматичні обмеження стосуються лише прямого виводу моделі.
{
"order_id": 4411,
"refund_amount_usd": 50,
"reason_code": "damaged",
"urgent": "yes"
}
Без закриття об'єкта зайві ключі проходять; після закриття їх відхилено, і помилка вказує на /reason_code.
Чотири: як закрити
Ajv: додайте additionalProperties: false на вузол об'єкта.
import Ajv from "ajv";
const ajv = new Ajv({ allErrors: true });
const schema = {
type: "object",
properties: { order_id: { type: "string" }, amount: { type: "number" } },
required: ["order_id", "amount"],
additionalProperties: false,
};
const validate = ajv.compile(schema);
validate({ order_id: "A1", amount: 50, reasn_code: "x" }); // false
Zod: z.strictObject або .strict(). Pydantic: ConfigDict(extra="forbid").
Спільне правило: закриття на кожному рівні. Кожен вкладений об'єкт потребує власного additionalProperties: false.
П'ять: політика приведення типів
Pydantic у lax-режимі перетворює "5" на 5. Три політики: сувора (відхиляти невідповідні типи), контрольована (тільки явні перетворення з межами), повністю вільна (лише для внутрішніх даних). Для ненадійних входів обирайте першу або другу й фіксуйте в тестах.
Шість: чекліст вкладеності
| Точка | Дія |
|---|---|
| Верхній об'єкт | additionalProperties: false |
| Кожен вкладений object | те саме |
| Елементи масивів | закривати об'єкти в items |
| Enum і діапазони | додати enum, minimum/maximum |
| Згенеровані схеми | доповнювати вручну |
Сім: після відхилення
allErrors: true показує всі поля одразу, verbose: true додає шляхи. Синтаксичні проблеми має ловити парсер до перевірки схеми.
FAQ
Чи не надто суворо?
Залежить від контракту. Для власних входів — закривати; для сторонніх даних з розширеннями — лише ядро.
Zod strip безпечніший?
Ні: він захищає нижні шари, але викликач не знає про втрату поля. Помилки мають з'являтися рано й явно.
strict-режим платформи замінює локальну перевірку?
Ні. Схема може бути відкритою, а граматика може не покривати аргументи інструментів.
Після закриття старі дані масово падають?
Це і є проблема, яку треба показати. Інвентаризуйте через allErrors, додайте нешкідливі поля, виправте шкідливі.
Однорядкове рішення?
Ні, але мінімальний крок: перевірити, що кожен type: "object" супроводжується additionalProperties: false.