JSON Toolkit

Sem additionalProperties, a validação de schema não valida nada

Resumo: Validação bem-sucedida não significa dados limpos. O Pydantic ignora campos desconhecidos por padrão, o Zod os remove e o Ajv deixa passar — parâmetros alucinados e chaves com erro de digitação atravessam em silêncio. Este artigo mostra as três formas de aceitação silenciosa e dá receitas completas: fechar objetos, controlar coerção e apertar cada nível de aninhamento.

Intermediate~8 min de leituraPublicado em 2026-11-02Atualizado em 2026-11-02
Um JSON com campos extras passando pela validação

Conclusão: por padrão, os validadores ignoram campos não declarados em vez de rejeitá-los. Sem additionalProperties: false, uma validação bem-sucedida só prova que os campos declarados estão corretos.

O comportamento mais perigoso de um validador não é levantar erro, é ficar em silêncio. Uma chave com erro de digitação ou um parâmetro inventado pelo modelo atravessa a validação sem barulho e só explode na lógica de negócio.

Um: uma validação que "passa"

{
  "type": "object",
  "properties": {
    "order_id": { "type": "string" },
    "amount": { "type": "number" }
  },
  "required": ["order_id", "amount"]
}

Envie {"order_id": "A1", "amount": 50, "reasn_code": "damaged"} — a terceira chave é um erro de digitação. Com a configuração padrão do Ajv, o resultado é sucesso. O campo extra não é rejeitado nem sinalizado.

Na especificação JSON Schema, o valor padrão de additionalProperties é true: campos não declarados são permitidos.

Dois: três sabores de silêncio

Runtime Comportamento padrão Consequência
Ajv (JS) deixa passar dados sujos chegam à lógica
Zod z.object remove o campo desaparece, o objeto encolhe
Pydantic v2 ignora (extra="ignore") idem, sem rastro nos logs

Remover é mais furtivo que deixar passar. Ambas as bibliotecas falham em silêncio da mesma forma, por padrão.

Três: chamadas de ferramentas LLM amplificam o problema

Os argumentos são gerados pelo modelo, que inventa nomes de campos e passa "3" como número. As plataformas definem parâmetros em JSON Schema, mas não forçam o fechamento do schema por padrão — o modo strict precisa ser ativado explicitamente, e as restrições gramaticais cobrem apenas a saída direta do modelo.

{
  "order_id": 4411,
  "refund_amount_usd": 50,
  "reason_code": "damaged",
  "urgent": "yes"
}

Com o objeto fechado, reason_code e urgent são rejeitados e o erro aponta para /reason_code.

Quatro: como fechar

Ajv: adicione additionalProperties: false no nó do objeto.

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 ou .strict(). Pydantic: ConfigDict(extra="forbid").

Regra comum: o fechamento é por nível. Cada objeto aninhado precisa do seu próprio additionalProperties: false.

Cinco: política de coerção

O modo lax do Pydantic converte "5" em 5. Três políticas: estrita (rejeitar tipos errados), controlada (apenas conversões explícitas e limitadas), permissiva (só dados internos). Para entradas não confiáveis, escolha a primeira ou a segunda e registre nos testes.

Seis: checklist de aninhamento

Ponto Ação
Objeto raiz additionalProperties: false
Cada objeto aninhado idem
Elementos de array fechar objetos dentro de items
Enums e faixas adicionar enum, minimum/maximum
Schemas gerados completar à mão

Sete: após a rejeição

allErrors: true reporta todos os campos de uma vez; verbose: true acrescenta caminhos. Problemas de sintaxe devem ser capturados antes, pelo parser.

FAQ

Não é estrito demais?

Depende do contrato. Para entradas próprias, feche; para dados de terceiros com extensões, feche o núcleo e isole a área de extensão.

O strip do Zod não é mais seguro?

Não: protege o downstream às custas do chamador. Erros devem aparecer cedo e explicitamente.

O modo strict da plataforma substitui a validação local?

Não. O schema pode continuar aberto e a gramática pode não cobrir os argumentos das ferramentas.

Dados antigos falham em massa depois de fechar?

É exatamente o que precisava ser exposto. Inventarie com allErrors, adicione campos inofensivos, corrija os nocivos.

Solução de uma linha?

Não, mas o gesto mínimo é: verificar que cada type: "object" vem seguido de additionalProperties: false.