FAILURE MAP
← Case archive

FA-72691 / Check-digit algorithms / Open access

UK VAT weights start at seven · case 01

Valid VAT numbers are reported invalid.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The weights come from range(7, 0, -1) instead of 8 down to 2.

VERIFIED REPAIR

Weight the seven leading digits 8, 7, 6, 5, 4, 3, 2.

Unsuccessful approach: Ascending weights 2..8 apply the right values to the wrong positions.

Case contract

UK VAT registration number check. Spaces are removed, the text is upper-cased and an optional GB prefix removed; the rest must be 9 ASCII digits (else "malformed"). Weights 8..2 apply to the first seven digits; the last two digits form a number c. Return "mod97" if (sum + c) is divisible by 97, "mod9755" if (sum + c + 55) is, else "invalid".

Why this case matters

Invoice processing screens supplier VAT numbers before reclaiming input tax.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(s):
    t = s.replace(' ', '').upper()
    if t.startswith('GB'):
        t = t[2:]
    if len(t) != 9 or not t.isascii() or not t.isdigit():
        return 'malformed'
    total = sum(int(ch) * w for ch, w in zip(t[:7], range(7, 0, -1)))
    c = int(t[7:])
    if (total + c) % 97 == 0:
        return 'mod97'
    if (total + c + 55) % 97 == 0:
        return 'mod9755'
    return 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["GB041839277"]', ['GB041839277'], 'mod97'], ['regression ["GB 261 7623 04"]', ['GB 261 7623 04'], 'mod9755'], ['partial-repair ["729236520"]', ['729236520'], 'mod97'], ['control ["041839278"]', ['041839278'], 'invalid'], ['control ["261762305"]', ['261762305'], 'invalid'], ['control ["729236521"]', ['729236521'], 'invalid'], ['control ["260407727"]', ['260407727'], 'invalid'], ['control ["029331391"]', ['029331391'], 'invalid']], [['regression ["GB260407726"]', ['GB260407726'], 'mod9755'], ['regression ["GB 029 3313 90"]', ['GB 029 3313 90'], 'mod97'], ['partial-repair ["581077575"]', ['581077575'], 'mod9755'], ['control ["581077576"]', ['581077576'], 'invalid'], ['control ["GB98078068"]', ['GB98078068'], 'malformed'], ['control ["GB98078068X"]', ['GB98078068X'], 'malformed'], ['control ["123456789"]', ['123456789'], 'invalid'], ['control ["041839278"]', ['041839278'], 'invalid']], [['regression ["GB658372602"]', ['GB658372602'], 'mod97'], ['regression ["GB 225 8573 89"]', ['GB 225 8573 89'], 'mod9755'], ['partial-repair ["331553089"]', ['331553089'], 'mod97'], ['control ["261762305"]', ['261762305'], 'invalid'], ['control ["729236521"]', ['729236521'], 'invalid'], ['control ["260407727"]', ['260407727'], 'invalid'], ['control ["029331391"]', ['029331391'], 'invalid'], ['control ["581077576"]', ['581077576'], 'invalid']], [['regression ["GB784073455"]', ['GB784073455'], 'mod9755'], ['regression ["GB 397 1093 27"]', ['GB 397 1093 27'], 'mod97'], ['partial-repair ["155142529"]', ['155142529'], 'mod9755'], ['control ["GB98078068"]', ['GB98078068'], 'malformed'], ['control ["GB98078068X"]', ['GB98078068X'], 'malformed'], ['control ["123456789"]', ['123456789'], 'invalid'], ['control ["041839278"]', ['041839278'], 'invalid'], ['control ["261762305"]', ['261762305'], 'invalid']], [['regression ["GB801525174"]', ['GB801525174'], 'mod97'], ['regression ["GB 307 8958 63"]', ['GB 307 8958 63'], 'mod9755'], ['partial-repair ["GB980780684"]', ['GB980780684'], 'mod97'], ['control ["729236521"]', ['729236521'], 'invalid'], ['control ["260407727"]', ['260407727'], 'invalid'], ['control ["029331391"]', ['029331391'], 'invalid'], ['control ["581077576"]', ['581077576'], 'invalid'], ['control ["GB98078068"]', ['GB98078068'], 'malformed']]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression ["GB041839277"]invalidmod97Failed
regression ["GB 261 7623 04"]invalidmod9755Failed
partial-repair ["729236520"]invalidmod97Failed
control ["041839278"]invalidinvalidPassed
control ["261762305"]invalidinvalidPassed
control ["729236521"]invalidinvalidPassed
control ["260407727"]invalidinvalidPassed
control ["029331391"]invalidinvalidPassed

SHA-256 / 44afd3875f747078e31bc30288922759f6cd7204cc4f67843bc9b193f12f3478

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(s):
    t = s.replace(' ', '').upper()
    if t.startswith('GB'):
        t = t[2:]
    if len(t) != 9 or not t.isascii() or not t.isdigit():
        return 'malformed'
    total = sum(int(ch) * w for ch, w in zip(t[:7], range(2, 9)))
    c = int(t[7:])
    if (total + c) % 97 == 0:
        return 'mod97'
    if (total + c + 55) % 97 == 0:
        return 'mod9755'
    return 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["GB041839277"]', ['GB041839277'], 'mod97'], ['regression ["GB 261 7623 04"]', ['GB 261 7623 04'], 'mod9755'], ['partial-repair ["729236520"]', ['729236520'], 'mod97'], ['control ["041839278"]', ['041839278'], 'invalid'], ['control ["261762305"]', ['261762305'], 'invalid'], ['control ["729236521"]', ['729236521'], 'invalid'], ['control ["260407727"]', ['260407727'], 'invalid'], ['control ["029331391"]', ['029331391'], 'invalid']], [['regression ["GB260407726"]', ['GB260407726'], 'mod9755'], ['regression ["GB 029 3313 90"]', ['GB 029 3313 90'], 'mod97'], ['partial-repair ["581077575"]', ['581077575'], 'mod9755'], ['control ["581077576"]', ['581077576'], 'invalid'], ['control ["GB98078068"]', ['GB98078068'], 'malformed'], ['control ["GB98078068X"]', ['GB98078068X'], 'malformed'], ['control ["123456789"]', ['123456789'], 'invalid'], ['control ["041839278"]', ['041839278'], 'invalid']], [['regression ["GB658372602"]', ['GB658372602'], 'mod97'], ['regression ["GB 225 8573 89"]', ['GB 225 8573 89'], 'mod9755'], ['partial-repair ["331553089"]', ['331553089'], 'mod97'], ['control ["261762305"]', ['261762305'], 'invalid'], ['control ["729236521"]', ['729236521'], 'invalid'], ['control ["260407727"]', ['260407727'], 'invalid'], ['control ["029331391"]', ['029331391'], 'invalid'], ['control ["581077576"]', ['581077576'], 'invalid']], [['regression ["GB784073455"]', ['GB784073455'], 'mod9755'], ['regression ["GB 397 1093 27"]', ['GB 397 1093 27'], 'mod97'], ['partial-repair ["155142529"]', ['155142529'], 'mod9755'], ['control ["GB98078068"]', ['GB98078068'], 'malformed'], ['control ["GB98078068X"]', ['GB98078068X'], 'malformed'], ['control ["123456789"]', ['123456789'], 'invalid'], ['control ["041839278"]', ['041839278'], 'invalid'], ['control ["261762305"]', ['261762305'], 'invalid']], [['regression ["GB801525174"]', ['GB801525174'], 'mod97'], ['regression ["GB 307 8958 63"]', ['GB 307 8958 63'], 'mod9755'], ['partial-repair ["GB980780684"]', ['GB980780684'], 'mod97'], ['control ["729236521"]', ['729236521'], 'invalid'], ['control ["260407727"]', ['260407727'], 'invalid'], ['control ["029331391"]', ['029331391'], 'invalid'], ['control ["581077576"]', ['581077576'], 'invalid'], ['control ["GB98078068"]', ['GB98078068'], 'malformed']]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression ["GB041839277"]invalidmod97Failed
regression ["GB 261 7623 04"]mod9755mod9755Passed
partial-repair ["729236520"]invalidmod97Failed
control ["041839278"]invalidinvalidPassed
control ["261762305"]invalidinvalidPassed
control ["729236521"]invalidinvalidPassed
control ["260407727"]invalidinvalidPassed
control ["029331391"]invalidinvalidPassed

SHA-256 / e1112f8861a42e8a2f5cf2b400c7b224a5f2fdf150f09935ef5e401cc5c63d67

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(s):
    t = s.replace(' ', '').upper()
    if t.startswith('GB'):
        t = t[2:]
    if len(t) != 9 or not t.isascii() or not t.isdigit():
        return 'malformed'
    total = sum(int(ch) * w for ch, w in zip(t[:7], range(8, 1, -1)))
    c = int(t[7:])
    if (total + c) % 97 == 0:
        return 'mod97'
    if (total + c + 55) % 97 == 0:
        return 'mod9755'
    return 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["GB041839277"]', ['GB041839277'], 'mod97'], ['regression ["GB 261 7623 04"]', ['GB 261 7623 04'], 'mod9755'], ['partial-repair ["729236520"]', ['729236520'], 'mod97'], ['control ["041839278"]', ['041839278'], 'invalid'], ['control ["261762305"]', ['261762305'], 'invalid'], ['control ["729236521"]', ['729236521'], 'invalid'], ['control ["260407727"]', ['260407727'], 'invalid'], ['control ["029331391"]', ['029331391'], 'invalid']], [['regression ["GB260407726"]', ['GB260407726'], 'mod9755'], ['regression ["GB 029 3313 90"]', ['GB 029 3313 90'], 'mod97'], ['partial-repair ["581077575"]', ['581077575'], 'mod9755'], ['control ["581077576"]', ['581077576'], 'invalid'], ['control ["GB98078068"]', ['GB98078068'], 'malformed'], ['control ["GB98078068X"]', ['GB98078068X'], 'malformed'], ['control ["123456789"]', ['123456789'], 'invalid'], ['control ["041839278"]', ['041839278'], 'invalid']], [['regression ["GB658372602"]', ['GB658372602'], 'mod97'], ['regression ["GB 225 8573 89"]', ['GB 225 8573 89'], 'mod9755'], ['partial-repair ["331553089"]', ['331553089'], 'mod97'], ['control ["261762305"]', ['261762305'], 'invalid'], ['control ["729236521"]', ['729236521'], 'invalid'], ['control ["260407727"]', ['260407727'], 'invalid'], ['control ["029331391"]', ['029331391'], 'invalid'], ['control ["581077576"]', ['581077576'], 'invalid']], [['regression ["GB784073455"]', ['GB784073455'], 'mod9755'], ['regression ["GB 397 1093 27"]', ['GB 397 1093 27'], 'mod97'], ['partial-repair ["155142529"]', ['155142529'], 'mod9755'], ['control ["GB98078068"]', ['GB98078068'], 'malformed'], ['control ["GB98078068X"]', ['GB98078068X'], 'malformed'], ['control ["123456789"]', ['123456789'], 'invalid'], ['control ["041839278"]', ['041839278'], 'invalid'], ['control ["261762305"]', ['261762305'], 'invalid']], [['regression ["GB801525174"]', ['GB801525174'], 'mod97'], ['regression ["GB 307 8958 63"]', ['GB 307 8958 63'], 'mod9755'], ['partial-repair ["GB980780684"]', ['GB980780684'], 'mod97'], ['control ["729236521"]', ['729236521'], 'invalid'], ['control ["260407727"]', ['260407727'], 'invalid'], ['control ["029331391"]', ['029331391'], 'invalid'], ['control ["581077576"]', ['581077576'], 'invalid'], ['control ["GB98078068"]', ['GB98078068'], 'malformed']]]
for label, args, expected in fixtures[N - 1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression ["GB041839277"]mod97mod97Passed
regression ["GB 261 7623 04"]mod9755mod9755Passed
partial-repair ["729236520"]mod97mod97Passed
control ["041839278"]invalidinvalidPassed
control ["261762305"]invalidinvalidPassed
control ["729236521"]invalidinvalidPassed
control ["260407727"]invalidinvalidPassed
control ["029331391"]invalidinvalidPassed

SHA-256 / a40501fb7419ea2a41995bf1b7d524120aa04a87897f22f482032d5cd24d29d7

Verification & scope

A deterministic, bounded teaching model of the named scheme under the stated contract; not a certified validator. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:48:40.816765+00:00.

Case digest / 16c28058d8469ae57ce077b04271d6d984c3c58cbaecf60a629d0d714e36a4ff