Senza additionalProperties, la validazione dello schema non valida nulla
In sintesi: Una validazione superata non significa dati puliti. Pydantic ignora i campi sconosciuti per impostazione predefinita, Zod li rimuove, Ajv li lascia passare — parametri allucinati e chiavi sbagliate attraversano in silenzio. L'articolo mostra le tre forme di accettazione silenziosa e fornisce ricette complete: chiudere gli oggetti, controllare la coercizione, serrare ogni livello di annidamento.

Conclusione: per impostazione predefinita i validatori ignorano i campi non dichiarati invece di rifiutarli. Senza additionalProperties: false, una validazione superata prova solo che i campi dichiarati vanno bene.
Il comportamento più pericoloso di un validatore non è sollevare un errore, ma tacere. Una chiave sbagliata o un parametro inventato dal modello attraversa la validazione senza un suono ed esplode nella logica di business.
Uno: una validazione che "passa"
{
"type": "object",
"properties": {
"order_id": { "type": "string" },
"amount": { "type": "number" }
},
"required": ["order_id", "amount"]
}
Inviare {"order_id": "A1", "amount": 50, "reasn_code": "damaged"} — la terza chiave è un errore di battitura. Con la configurazione predefinita di Ajv il risultato è successo. Il campo extra non viene né rifiutato né segnalato.
Nella specifica JSON Schema il valore predefinito di additionalProperties è true: i campi non dichiarati sono ammessi.
Due: tre varianti del silenzio
| Runtime | Comportamento predefinito | Consequenza |
|---|---|---|
| Ajv (JS) | lascia passare | dati sporchi raggiungono la logica |
Zod z.object |
rimuove | il campo sparisce, l'oggetto si accorcia |
| Pydantic v2 | ignora (extra="ignore") |
idem, nessuna traccia nei log |
La rimozione è più furtiva del passaggio. Entrambe le librerie falliscono silenziosamente allo stesso modo, per impostazione predefinita.
Tre: le chiamate di strumenti LLM amplificano il problema
Gli argomenti sono generati dal modello, che inventa nomi di campi e passa "3" come numero. Le piattaforme definiscono i parametri in JSON Schema ma non forzano la chiusura dello schema — la modalità strict va attivata esplicitamente, e i vincoli grammaticali coprono solo l'output diretto del modello.
{
"order_id": 4411,
"refund_amount_usd": 50,
"reason_code": "damaged",
"urgent": "yes"
}
Con l'oggetto chiuso, reason_code e urgent vengono rifiutati e l'errore punta a /reason_code.
Quattro: come chiudere
Ajv: aggiungere additionalProperties: false sul nodo dell'oggetto.
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 o .strict(). Pydantic: ConfigDict(extra="forbid").
Regola comune: la chiusura è per livello. Ogni oggetto annidato richiede il proprio additionalProperties: false.
Cinque: politica di coercizione
La modalità lax di Pydantic converte "5" in 5. Tre politiche: stretta (rifiutare i tipi errati), controllata (solo conversioni esplicite e limitate), permissiva (solo dati interni). Per input non affidabili scegliere la prima o la seconda e codificarla nei test.
Sei: checklist di annidamento
| Punto | Azione |
|---|---|
| Oggetto radice | additionalProperties: false |
| Ogni oggetto annidato | idem |
| Elementi di array | chiudere gli oggetti in items |
| Enum e intervalli | aggiungere enum, minimum/maximum |
| Schemi generati | completare a mano |
Sette: dopo il rifiuto
allErrors: true segnala tutti i campi insieme; verbose: true aggiunge i percorsi. I problemi di sintassi li cattura il parser prima della validazione dello schema.
FAQ
Troppo severo?
Dipende dal contratto. Per input propri, chiudere; per dati di terzi estensibili, chiudere il nucleo e isolare l'area di estensione.
Lo strip di Zod non è più sicuro?
No: protegge il downstream a spese del chiamante. Gli errori devono emergere presto ed esplicitamente.
La modalità strict della piattaforma sostituisce la validazione locale?
No. Lo schema può restare aperto e la grammatica può non coprire gli argomenti degli strumenti.
I vecchi dati falliscono in massa dopo la chiusura?
È esattamente ciò che andava esposto. Inventariare con allErrors, aggiungere i campi innocui, correggere gli altri.
Una soluzione in una riga?
No, ma il gesto minimo: verificare che ogni type: "object" sia seguito da additionalProperties: false.