bateCarimbo.
Erros comuns

Dez erros comuns em validadores de documentos brasileiros (e como testá-los)

Aceitar dígitos repetidos, errar o tratamento do resto 10, gravar o documento como número, usar regex só numérica depois do CNPJ alfanumérico, rejeitar RG de outro estado: cada erro com sintoma, causa, teste de reprodução e correção.

Por · Publicado em · 11 min de leitura

Validador de documento é código que quase ninguém revisa: sai de um gist, passa nos dois ou três números testados à mão e vai para produção. O defeito aparece meses depois, disfarçado de outra coisa — um cliente que não conclui o cadastro, um relatório com um CPF que concentra centenas de pedidos, uma importação que rejeita metade das linhas sem dizer por quê. Abaixo, dez defeitos recorrentes com o número exato que reproduz cada um.

1. Aceitar sequências de dígitos repetidos

Sintoma

111.111.111-11 e 000.000.000-00 aparecem no banco, às vezes centenas de vezes, e viram um cliente fantasma que agrega pedidos de pessoas diferentes.

Causa

O módulo 11 não tem nenhuma objeção a repetição. Na base 111111111, a soma ponderada dá 54, o resto é 10 e o dígito sai 1; a segunda passada soma 65, resto 10, dígito 1 outra vez. A conta fecha. E isso não é um acidente de um número só: as dez sequências repetidas de CPF passam no cálculo, de 00000000000 a 99999999999.

Como reproduzir

Repita cada algarismo onze vezes em um laço e passe pela sua função. No CNPJ numérico o estrago é menor — só 00.000.000/0000-00 passa, porque todas as somas dão zero —, mas é justamente o valor que mais aparece em formulário abandonado.

Correção

Uma checagem explícita antes do cálculo: /^(\d)\1{10}$/ para CPF e \1{13} para CNPJ. Para ter contraexemplos que passem pelo motivo certo, use o gerador de CPF, que nunca emite sequência trivial.

2. Errar o tratamento do resto 10 ou 11

Sintoma

Uma fatia pequena e teimosa de documentos legítimos é recusada. A proporção costuma ficar perto de um em onze, o que dá a impressão de aleatoriedade.

Causa

Cada documento resolve o resto de um jeito e o código copiado resolve do jeito do documento anterior. CPF e CNPJ: resto menor que 2 vira dígito 0, caso contrário o dígito é 11 − resto. RG no padrão SSP-SP: o dígito é sempre 11 − resto, com 10 virando X e 11 virando 0. PIS: resto 10 produz o dígito 1. RENAVAM nem usa o resto direto — multiplica a soma por 10 antes do módulo.

Como reproduzir

O RG 24.598.973-0 é o caso limite: o resto é 0, então 11 − 0 = 11, e 11 precisa virar 0. Quem esquece essa conversão grava o dígito 11 ou rejeita o documento.

RG 24.598.973-0 — o resto zero
CaracterePesoProduto
224
4312
5420
9545
8648
9763
7856
3927
Soma275

275 ÷ 11 → resto 0

11 − 0 = 11, e 11 vira 0 (no padrão SSP-SP, 10 viraria X).

Dígito verificador = 0

Correção

Isolar o tratamento do resto em uma função por documento, em vez de espalhar if dentro do laço, e montar o lote de teste com os geradores de RG, PIS e RENAVAM, que evitam as bases em que validadores conhecidos divergem. O passo a passo da conta está no guia do módulo 11; a CNH tem uma zona de divergência própria, descrita no guia do número de registro da CNH.

TypeScript
// CPF e CNPJ
const dvCpf = (resto: number) => (resto < 2 ? 0 : 11 - resto);

// RG (SSP-SP): 10 vira X, 11 vira 0
const dvRg = (resto: number) => {
  const d = 11 - resto;
  return d === 10 ? "X" : d === 11 ? "0" : String(d);
};

// PIS/PASEP: resto 10 devolve 1; resto 0 ou 1 devolvem 0
const dvPis = (resto: number) => (resto < 2 ? 0 : 11 - resto);

// RENAVAM: a soma é multiplicada por 10 antes do módulo
const dvRenavam = (soma: number) => {
  const d = (soma * 10) % 11;
  return d >= 10 ? 0 : d;
};
A soma ponderada é a mesma; o que muda entre documentos é só a última linha.

3. Não remover a máscara antes de validar

Sintoma

O mesmo CPF é aceito no formulário do site e recusado pela API, ou o contrário. Importação de planilha rejeita tudo.

Causa

A função verifica o comprimento antes de normalizar. Com pontuação, 123.456.789-09 tem 14 caracteres, não 11. O inverso também acontece: validador que espera máscara e recebe 12345678909.

Como reproduzir

Quatro formas do mesmo documento: 123.456.789-09, 12345678909, 123 456 789 09 e a string com espaço e quebra de linha no fim, que é o que chega de um copiar e colar de planilha. As quatro têm de dar o mesmo resultado.

Correção

Normalize na fronteira do sistema, valide depois, guarde normalizado e aplique a máscara só na exibição. Atenção a replace(/\D/g, ""): essa regex serve para CPF, mas destrói CNPJ com letras — veja o erro 5. Os padrões prontos de cada documento estão no guia de regex para documentos brasileiros.

4. Gravar o documento em coluna numérica

Sintoma

Registros com CPF de dez dígitos no banco. Cerca de um em cada dez cadastros quebra, sempre os de CPF que começa com zero.

Causa

BIGINT guarda um número, e número não tem zero à esquerda. O CPF 012.345.678-90, normalizado para 01234567890, volta do banco como 1234567890 e falha na validação seguinte por comprimento. O mesmo vale para CEP que começa com zero, como toda a capital paulista, e para RENAVAM antigo.

Como reproduzir

Insira 01234567890 em uma coluna inteira, leia de volta e compare o comprimento. São dez caracteres.

Correção

Documento é identificador, não quantidade: nunca entra em soma, média ou comparação de ordem. Coluna de texto com comprimento fixo — e o CNPJ alfanumérico encerra a discussão, já que não cabe em campo numérico.

SQL — PostgreSQL
-- errado: perde o zero à esquerda
cpf BIGINT NOT NULL

-- certo: comprimento fixo, sem máscara
cpf   CHAR(11) NOT NULL CHECK (cpf ~ '^[0-9]{11}$'),
cnpj  CHAR(14) NOT NULL CHECK (cnpj ~ '^[0-9A-Z]{12}[0-9]{2}$')
O CHECK só é possível porque a coluna é texto.

5. Regex só numérica depois do CNPJ alfanumérico

Sintoma

Desde julho de 2026, inscrições novas passam a chegar com letras e o sistema responde “CNPJ inválido” na entrada — ou, pior, aceita e grava lixo.

Causa

Duas linhas herdadas do formato antigo: /^\d{14}$/ e replace(/\D/g, ""). A primeira recusa; a segunda apaga as letras em silêncio e transforma 12.ABC.345/01DE-35 em 123450135, nove caracteres que não são CNPJ nenhum.

Como reproduzir

Use o exemplo oficial do manual da Receita: 12.ABC.345/01DE-35. Os doze primeiros caracteres são a base, os dois últimos continuam sendo algarismos — os dígitos verificadores nunca são letras.

Correção

Normalizar para maiúsculas, validar com ^[0-9A-Z]{12}\d{2}$ e converter cada caractere pelo código ASCII menos 48 antes de multiplicar pelos pesos, o que faz A valer 17. A regra completa, com a conta resolvida, está no guia do CNPJ alfanumérico; para gerar números dos dois formatos, o gerador de CNPJ alterna entre eles.

6. Confundir documento válido com documento existente

Sintoma

O cadastro aprova o CPF, mas a emissão de nota, a consulta de crédito ou a conciliação falham depois, com o cliente já dentro do sistema.

Causa

O dígito verificador prova coerência interna, nada mais. Ele diz que os dois últimos algarismos combinam com os nove anteriores. Não diz que existe inscrição, que ela está ativa nem que pertence a quem digitou.

Como reproduzir

Gere um número no gerador de CPF e rode seu validador: aprovado. Agora procure esse mesmo número em qualquer base que dependa de inscrição real. Ele não está lá — e é exatamente por isso que serve como dado de teste.

Correção

Tratar formato, dígito verificador e existência como três verificações distintas, cada uma com sua mensagem de erro. Só a terceira depende de consulta oficial, é lenta e pode falhar. O detalhamento está em CPF válido não é CPF existente.

7. Rejeitar RG emitido em outro estado

Sintoma

Usuários de fora de São Paulo travam no campo de RG. O suporte recebe prints de documentos legítimos marcados em vermelho.

Causa

O algoritmo de oito dígitos com pesos 2 a 9 é a convenção da SSP-SP, a mais difundida pelos validadores da internet — e nada além disso. Não há padrão nacional de RG: cada secretaria estadual define comprimento, formatação e regra de conferência, e há emissores sem dígito verificador. Um validador de RG “genérico” é, na prática, um validador de São Paulo.

Como reproduzir

Mesmo dentro de SP a restrição costuma estar errada: /^\d{9}$/ rejeita o RG 12.345.671-X, cuja soma é 177 e o resto 1, o que leva o dígito a 10 e, por convenção, à letra X. Gere lotes no gerador de RG até aparecer um terminado em X e passe pelo seu validador.

Correção

RG como campo de texto, com limite de comprimento e órgão emissor ao lado; o cálculo do dígito só quando o emissor for SSP-SP. Onde o sistema precisa de identificador único de pessoa física, o campo certo é o CPF, que a Carteira de Identidade Nacional adotou como número único. As diferenças por estado estão em RG por estado e a CIN.

8. Não aceitar RENAVAM de nove dígitos

Sintoma

A importação de uma frota antiga rejeita justamente os veículos mais velhos, e sempre os mesmos.

Causa

O RENAVAM passou a ser exibido com onze dígitos a partir de 2013. Documentos anteriores trazem nove, e é o mesmo número: os zeros à esquerda ficam implícitos no impresso. Um validador com /^\d{11}$/ recusa metade do acervo histórico.

Como reproduzir

Pegue 123456789, nove dígitos. Completado à esquerda, vira 00123456789. A base 0012345678, com os pesos 3, 2, 9, 8, 7, 6, 5, 4, 3 e 2, soma 156; multiplicada por 10 dá 1560, e 1560 mod 11 = 9, exatamente o último dígito. O número é válido nas duas formas.

Correção

Aceitar de 9 a 11 dígitos na entrada, completar com zeros à esquerda até onze e só então calcular. É uma linha: valor.padStart(11, "0"). O gerador de RENAVAM emite no formato de onze, que é o que você deve gravar.

9. Ignorar a exceção de SP e MG no título de eleitor

Sintoma

Títulos de eleitores paulistas e mineiros são recusados sem padrão aparente, enquanto os dos outros estados passam.

Causa

O título usa módulo 11 com resto 10 convertido em 0, como de praxe. Só que, nos títulos de São Paulo e Minas Gerais — códigos 01 e 02 nos dígitos 9 e 10 —, o resto 0 vira 1. Quem implementa a regra geral erra apenas nesses dois estados, e apenas nos sequenciais em que algum resto dá zero.

Como reproduzir

Sequencial 12345677 com o código de UF 01. Os pesos 2 a 9 somam 231, que é múltiplo de 11, então o resto é 0. Com a exceção, o primeiro dígito é 1 e o segundo sai de 7×0 + 8×1 + 9×1 = 17, resto 6. Sem a exceção, o primeiro é 0 e o segundo vem de 8, resto 8. Dois títulos distintos para a mesma base: 1234 5677 0116 e 1234 5677 0108. Apenas o primeiro segue a regra do TSE.

Correção

Aplicar a conversão de resto 0 em 1 só quando os dígitos 9 e 10 forem 01 ou 02, nos dois dígitos verificadores. O gerador de título permite fixar a UF, o que facilita montar o lote de teste; a estrutura completa do número está no guia do título de eleitor.

10. Validar no cliente e confiar nisso no servidor

Sintoma

Documentos inválidos que, segundo o time, “não deveriam existir” aparecem no banco. Costumam vir de integração servidor-a-servidor, de uma versão antiga do aplicativo ou de um script de carga.

Causa

A validação em JavaScript no navegador é conveniência de interface: evita uma ida ao servidor para avisar o usuário do erro de digitação. Qualquer cliente que fale HTTP ignora essa camada.

Como reproduzir

Um curl direto no endpoint de cadastro com 123.456.789-00 — formato certo, dígitos errados. Se o registro for gravado, a regra existia só no front.

Correção

A mesma função nas duas pontas, de preferência no mesmo pacote compartilhado, mais uma restrição no banco como última linha de defesa. As implementações estão em JavaScript e TypeScript, Python, Java e C# e SQL.

Uma suíte mínima de testes

Nove dos dez defeitos acima morrem com uma tabela de casos. O que ela precisa cobrir, por documento: um número válido com máscara e o mesmo sem máscara; um número com zero à esquerda; um dígito verificador trocado; as sequências repetidas; o caso de resto limite do documento; e o comprimento fora da faixa. Fixe os valores no código em vez de gerar aleatoriamente dentro do teste — um teste que sorteia a própria entrada falha em dias diferentes e não diz por quê.

TypeScript — Vitest
const CASOS: [string, boolean, string][] = [
  ["123.456.789-09", true,  "válido com máscara"],
  ["12345678909",    true,  "válido sem máscara"],
  ["012.345.678-90", true,  "zero à esquerda"],
  ["123.456.789-00", false, "segundo dígito trocado"],
  ["111.111.111-11", false, "sequência repetida"],
  ["1234567890",     false, "curto demais"],
];

describe("validarCpf", () => {
  it.each(CASOS)("%s → %s (%s)", (entrada, esperado) => {
    expect(validarCpf(entrada)).toBe(esperado);
  });

  it("rejeita as dez sequências repetidas", () => {
    for (let d = 0; d <= 9; d++) {
      expect(validarCpf(String(d).repeat(11))).toBe(false);
    }
  });
});
Os mesmos casos servem para Jest, pytest ou JUnit: o que importa é a tabela.

O décimo defeito — confiar na validação do cliente — não aparece em teste unitário, porque a função em si está correta. Ele só aparece num teste de integração que bate no endpoint sem passar pela tela. Vale escrever esse teste uma vez por rota que aceite documento.

Para montar a massa de entrada, o gerador de pessoas devolve fichas coerentes — CPF, RG, CEP e data de nascimento combinando entre si — e o gerador de empresas faz o mesmo do lado da pessoa jurídica. Os números passam no cálculo do dígito verificador e não correspondem a ninguém.

Fontes e referências