From 2fb251996798798af38903fe535b8d147084a50a Mon Sep 17 00:00:00 2001 From: Murilo Ijanc Date: Mon, 27 Jul 2026 20:33:51 -0300 Subject: Expose the municipality behind a not-found CEP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A CEP that fails a lookup is rarely random: it is usually someone who knows the city and guessed the number, and the classic guess is the -000 that 957 municipalities never received. The API now names, on the 404 itself, the municipality that CEP would have belonged to, so a caller can say "not found — did you mean somewhere in Areal/RJ?" instead of just "not found". CepNotFound carries it as an optional Faixa. It stays an error: the block is context, not an address, and the range it describes is sparse, which is why it reports a count alongside the first and last CEP that actually exist there rather than the bounds the Correios publish. Parsing it is best-effort on purpose — a missing, empty or malformed block leaves the optional empty, since a 404 is already the answer and reading the hint attached to it must never cost the diagnosis. --- README.md | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'README.md') diff --git a/README.md b/README.md index 0b85199..876a819 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ make**, not by the HTTP status that caused it: | error | when | does retrying help? | | --- | --- | --- | | `InvalidCep` | not 8 digits | no | -| `CepNotFound` | well-formed, not in the database | no | +| `CepNotFound` | well-formed, not in the database; may expose `faixa()` | no | | `RateLimited` | 429; exposes `retry_after()` | yes, later | | `ServerError` | 5xx; exposes `status()` | yes, later | | `NetworkError` | DNS, connection, timeout, TLS | yes, later | @@ -209,6 +209,44 @@ try { } ``` +### A CEP that does not exist + +`CepNotFound` often knows **where the CEP would have been**. A CEP that fails +is rarely random: it is usually someone who knows the city and guessed the +number, and the classic guess is the `-000` that 957 municipalities never +received. + +```cpp +try { + const cepis::Address a = cepis::lookup("25845-000"); +} catch (const cepis::CepNotFound& error) { + if (error.faixa()) { + error.faixa()->localidade; // "Areal" + error.faixa()->uf; // "RJ" + error.faixa()->ibge; // "3300225" + error.faixa()->cep_ini; // "25845001" + error.faixa()->cep_fim; // "25849899" + error.faixa()->ceps; // 118 + } +} +``` + +Three things to keep straight: + +- **It is still a 404.** `faixa` is context for an error, not an address. The + CEP does not exist; nothing here makes it deliverable. +- **`cep_ini` and `cep_fim` are the CEPs that actually exist**, not the bounds + the Correios publish. The DNE's own start for Areal is `25845000` — exactly + the CEP that just failed. +- **The range is sparse.** Areal's 118 CEPs are scattered across the ~4,900 + numbers between `25845001` and `25849899`. That is what `ceps` is for; never + present the pair as a list of valid CEPs. + +When the CEP falls in a band no municipality owns, the API **omits** `faixa` +entirely — it never sends null — and `error.faixa()` is empty. A block that +cannot be read is empty too: a 404 is already the answer, and failing to read +the hint attached to it never turns into a parse error. + `InvalidResponse` almost always means something between you and the API is answering instead of it — a captive Wi-Fi portal, a corporate proxy, an HTML error page. Useful as a diagnosis: the problem is not in your code. -- cgit v1.3