How to Stop an AI Agent from Inventing Nigerian Postcodes

How to Stop an AI Agent from Inventing Nigerian Postcodes


On 1 October 2026, Nigeria's postcode system changed from six digits to an 11-character code that identifies a single building, such as EK-01-A03-FK-01.


Language models trained before that date still describe the old format with full confidence. That affects two kinds of agent:


  • A coding agent that writes your validation code
  • A runtime agent that reads a postcode from a customer's message or fills in a delivery form

This article shows you how to contain both. For the coding agent, you'll measure what it says today and give it a rule that corrects it. For the runtime agent, you'll build a boundary: the model proposes a postcode, and deterministic code decides whether it's well formed, whether NIPOST has assigned it, and where the work goes next.


You'll also see the tools an agent can call directly and a table of every failure state. The examples use ng-postcode, an open-source library I maintain, and its MCP server.


Table of Contents



Prerequisites


Before you begin, make sure you have:


  • Node.js 20 or later (for the ng-postcode library and MCP server)
  • An active NIPOST API credential (for runtime code confirmation)
  • Access to your agent framework of choice (OpenAI, Anthropic, or a local model)

What a Coding Agent Says Today


Ask a pre-2026 language model to validate a Nigerian postcode and it will almost certainly produce a regex like /^\d{6}$/. That pattern was correct for decades, but it now rejects every valid code issued since 1 October 2026.


Worse, the model rarely flags this as a guess. It presents the stale pattern as authoritative, which is exactly the behaviour that makes hallucinated postcodes dangerous in production systems.


How to Correct the Coding Agent at the Source


Rather than patching the model's output after the fact, give it a canonical reference. Point the coding agent at the ng-postcode library and its published format specification. When the agent has access to the current grammar, most modern models will generate correct validation logic on the first try.


Key corrections to embed in your agent's system prompt:


  • The code is 11 characters, not 6 digits
  • The format is STATE-AREA-DISTRICT-BUILDING-SUFFIX
  • Example: EK-01-A03-FK-01
  • The old 6-digit format is deprecated and must not be accepted as valid

Where to Draw the Boundary


The core principle behind preventing hallucinated postcodes is simple: the model proposes, deterministic code decides. A runtime agent should never be trusted to determine whether a postcode is real. Instead, its role is limited to extracting a candidate string from unstructured input. Verification is a separate, non-LLM step.


This split matters because it isolates the failure mode. If the model invents a code, the gate catches it before anything downstream is affected.


How to Build the Gate


Using ng-postcode, a runtime gate looks like this:


  1. Extract โ€” the agent pulls a candidate postcode from the customer message.
  2. Well-formedness check โ€” apply the 11-character grammar.
  3. Existence check โ€” call NIPOST to confirm the code is assigned.
  4. Route โ€” on success, dispatch to the assigned delivery zone; on failure, escalate to a human or ask the customer to re-enter.

  5. The gate never asks the model whether a code is real. It only asks the model for a string.


    How to Confirm a Code with NIPOST


    Once a candidate passes the format check, send it to the NIPOST lookup endpoint. A well-formed but unassigned code is a common failure โ€” attackers and language models alike can generate plausible-looking strings that were never issued.


    The confirmation step returns one of three results:


    • Assigned โ€” the code exists and maps to a specific building
    • Reserved โ€” the code is structurally valid but not yet in use
    • Unknown โ€” the code is not recognised by NIPOST

    Treat anything other than assigned as a routing failure.


    How to Route Work by Prefix


    The first segment of a Nigerian postcode identifies the state. EK is Ekiti, LA is Lagos, and so on. Once a code is confirmed, you can route the request to the correct regional handler without a second lookup.


    This is useful for agents that fan out work to regional queues, and it gives you a cheap sanity check: if the agent extracts EK-... but the customer's stated address is in Lagos, you have a mismatch worth surfacing.


    How to Put the Steps Together


    A minimal runtime flow:


    customer message
        โ†’ agent extracts candidate string
        โ†’ format gate (ng-postcode)
        โ†’ NIPOST lookup
            โ†’ assigned: route to state queue
            โ†’ reserved/unknown: escalate to human
    

    Every failure path returns a deterministic, auditable outcome rather than a fabricated success.


    How to Give an Agent Tools with Enforced Limits


    The ng-postcode MCP server exposes two tools to an agent:


    • validate_postcode(code) โ€” returns format and assignment status
    • route_postcode(code) โ€” returns the target state and queue

    Neither tool accepts free-form input beyond the candidate string, and neither delegates the correctness decision back to the model. This is the shape every postcode-related tool should take: narrow inputs, verifiable outputs, no room for the model to confabulate.


    How to Handle Every Failure State


    | State | Meaning | Action |

    | --- | --- | --- |

    | Malformed | Does not match the 11-character grammar | Ask customer to re-enter |

    | Well-formed but unknown | Passes format, not assigned by NIPOST | Escalate to human |

    | Reserved | Structurally valid, not yet issued | Escalate to human |

    | Assigned | Confirmed by NIPOST | Route by state prefix |

    | Mismatch | Code state conflicts with stated address | Escalate to human |


    Log every state, including the successful ones. When a language model invents a postcode, the log is how you find out โ€” and how you prove the gate did its job.

    via FreeCodeCamp

Related