FAILURE MAP
← Case archive

FA-72481 / Check-digit algorithms / Open access

IBAN accepts unknown countries of any length · case 01

Identifiers from unsupported countries and truncated IBANs get a mod-97 verdict.

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

ROOT CAUSE

The length lookup defaults to the actual length, so any country code passes.

VERIFIED REPAIR

Require the length registered for the country; unknown countries are malformed.

Unsuccessful approach: A generic 34-character ceiling still admits unknown countries and wrong lengths.

Case contract

IBAN validation for the countries DE 22, GB 22, FR 27, NL 18, BE 16, NO 15, MT 31 (other countries: "malformed"). Spaces (print format) are removed and letters are upper-cased. The check digits must be 02..98. Move the first four characters to the end, replace letters with 10..35, and the IBAN is "valid" when the number is 1 mod 97.

Why this case matters

Payment initiation rejects mistyped account identifiers before submitting transfers.

1 / The failure

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

N = 1
observations = []
def solve(s):
    LENGTHS = {'DE': 22, 'GB': 22, 'FR': 27, 'NL': 18, 'BE': 16, 'NO': 15, 'MT': 31}
    t = s.replace(' ', '').upper()
    if len(t) < 5 or not t.isascii() or not t.isalnum():
        return 'malformed'
    if not t[:2].isalpha() or not t[2:4].isdigit() or not 2 <= int(t[2:4]) <= 98:
        return 'malformed'
    if LENGTHS.get(t[:2], len(t)) != len(t):
        return 'malformed'
    r = t[4:] + t[:4]
    n = ''.join(str(int(ch, 36)) for ch in r)
    return 'valid' if int(n) % 97 == 1 else 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['control ["DE59811903138375291124"]', ['DE59811903138375291124'], 'valid'], ['control ["DE50296496123032354476"]', ['DE50296496123032354476'], 'valid'], ['control ["DE58862288567935243525"]', ['DE58862288567935243525'], 'valid'], ['control ["GB12YUBO17171961366336"]', ['GB12YUBO17171961366336'], 'valid'], ['control ["GB30TWRL09177880233695"]', ['GB30TWRL09177880233695'], 'valid'], ['control ["GB23HJGZ79203524065161"]', ['GB23HJGZ79203524065161'], 'valid']], [['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['control ["GB23HJGZ79203524065161"]', ['GB23HJGZ79203524065161'], 'valid'], ['control ["FR88HIO0QY01NRB0VCRB14QO450"]', ['FR88HIO0QY01NRB0VCRB14QO450'], 'valid'], ['control ["FR38Q7OJKEPW4X8D3YP3O6RO9CF"]', ['FR38Q7OJKEPW4X8D3YP3O6RO9CF'], 'valid'], ['control ["FR87B5LPN0GWM8FRHG96JYM18V0"]', ['FR87B5LPN0GWM8FRHG96JYM18V0'], 'valid'], ['control ["NL34HYUK1800961826"]', ['NL34HYUK1800961826'], 'valid'], ['control ["NL15KCEZ0926524218"]', ['NL15KCEZ0926524218'], 'valid']], [['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['control ["NL15KCEZ0926524218"]', ['NL15KCEZ0926524218'], 'valid'], ['control ["NL97OZQU5858488442"]', ['NL97OZQU5858488442'], 'valid'], ['control ["BE73177155519075"]', ['BE73177155519075'], 'valid'], ['control ["BE31287393626938"]', ['BE31287393626938'], 'valid'], ['control ["BE87587165649240"]', ['BE87587165649240'], 'valid'], ['control ["NO5861733992430"]', ['NO5861733992430'], 'valid']], [['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['control ["NO5861733992430"]', ['NO5861733992430'], 'valid'], ['control ["NO7438063392154"]', ['NO7438063392154'], 'valid'], ['control ["NO8414946034784"]', ['NO8414946034784'], 'valid'], ['control ["MT48QBGB31246251447244909393188"]', ['MT48QBGB31246251447244909393188'], 'valid'], ['control ["MT24EODH27387088780936299640502"]', ['MT24EODH27387088780936299640502'], 'valid'], ['control ["MT98JHCG63199638448964680432116"]', ['MT98JHCG63199638448964680432116'], 'valid']], [['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['control ["MT98JHCG63199638448964680432116"]', ['MT98JHCG63199638448964680432116'], 'valid'], ['control ["DE59811903138375291127"]', ['DE59811903138375291127'], 'invalid'], ['control ["DE50296496123032354477"]', ['DE50296496123032354477'], 'invalid'], ['control ["DE58862288567935243527"]', ['DE58862288567935243527'], 'invalid'], ['control ["GB12YUBO17171961366337"]', ['GB12YUBO17171961366337'], 'invalid'], ['control ["GB30TWRL09177880233697"]', ['GB30TWRL09177880233697'], 'invalid']]]
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 ["XX82WEST12345698765432"]invalidmalformedFailed
regression ["US64SVBKUS6S3300958879"]validmalformedFailed
control ["DE59811903138375291124"]validvalidPassed
control ["DE50296496123032354476"]validvalidPassed
control ["DE58862288567935243525"]validvalidPassed
control ["GB12YUBO17171961366336"]validvalidPassed
control ["GB30TWRL09177880233695"]validvalidPassed
control ["GB23HJGZ79203524065161"]validvalidPassed

SHA-256 / cc3ef415765723a43e84a3a3378d42e4f89bcc7a11c77477ac4b35ad6f5383bc

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(s):
    LENGTHS = {'DE': 22, 'GB': 22, 'FR': 27, 'NL': 18, 'BE': 16, 'NO': 15, 'MT': 31}
    t = s.replace(' ', '').upper()
    if len(t) < 5 or not t.isascii() or not t.isalnum():
        return 'malformed'
    if not t[:2].isalpha() or not t[2:4].isdigit() or not 2 <= int(t[2:4]) <= 98:
        return 'malformed'
    if len(t) > 34:
        return 'malformed'
    r = t[4:] + t[:4]
    n = ''.join(str(int(ch, 36)) for ch in r)
    return 'valid' if int(n) % 97 == 1 else 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['control ["DE59811903138375291124"]', ['DE59811903138375291124'], 'valid'], ['control ["DE50296496123032354476"]', ['DE50296496123032354476'], 'valid'], ['control ["DE58862288567935243525"]', ['DE58862288567935243525'], 'valid'], ['control ["GB12YUBO17171961366336"]', ['GB12YUBO17171961366336'], 'valid'], ['control ["GB30TWRL09177880233695"]', ['GB30TWRL09177880233695'], 'valid'], ['control ["GB23HJGZ79203524065161"]', ['GB23HJGZ79203524065161'], 'valid']], [['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['control ["GB23HJGZ79203524065161"]', ['GB23HJGZ79203524065161'], 'valid'], ['control ["FR88HIO0QY01NRB0VCRB14QO450"]', ['FR88HIO0QY01NRB0VCRB14QO450'], 'valid'], ['control ["FR38Q7OJKEPW4X8D3YP3O6RO9CF"]', ['FR38Q7OJKEPW4X8D3YP3O6RO9CF'], 'valid'], ['control ["FR87B5LPN0GWM8FRHG96JYM18V0"]', ['FR87B5LPN0GWM8FRHG96JYM18V0'], 'valid'], ['control ["NL34HYUK1800961826"]', ['NL34HYUK1800961826'], 'valid'], ['control ["NL15KCEZ0926524218"]', ['NL15KCEZ0926524218'], 'valid']], [['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['control ["NL15KCEZ0926524218"]', ['NL15KCEZ0926524218'], 'valid'], ['control ["NL97OZQU5858488442"]', ['NL97OZQU5858488442'], 'valid'], ['control ["BE73177155519075"]', ['BE73177155519075'], 'valid'], ['control ["BE31287393626938"]', ['BE31287393626938'], 'valid'], ['control ["BE87587165649240"]', ['BE87587165649240'], 'valid'], ['control ["NO5861733992430"]', ['NO5861733992430'], 'valid']], [['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['control ["NO5861733992430"]', ['NO5861733992430'], 'valid'], ['control ["NO7438063392154"]', ['NO7438063392154'], 'valid'], ['control ["NO8414946034784"]', ['NO8414946034784'], 'valid'], ['control ["MT48QBGB31246251447244909393188"]', ['MT48QBGB31246251447244909393188'], 'valid'], ['control ["MT24EODH27387088780936299640502"]', ['MT24EODH27387088780936299640502'], 'valid'], ['control ["MT98JHCG63199638448964680432116"]', ['MT98JHCG63199638448964680432116'], 'valid']], [['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['control ["MT98JHCG63199638448964680432116"]', ['MT98JHCG63199638448964680432116'], 'valid'], ['control ["DE59811903138375291127"]', ['DE59811903138375291127'], 'invalid'], ['control ["DE50296496123032354477"]', ['DE50296496123032354477'], 'invalid'], ['control ["DE58862288567935243527"]', ['DE58862288567935243527'], 'invalid'], ['control ["GB12YUBO17171961366337"]', ['GB12YUBO17171961366337'], 'invalid'], ['control ["GB30TWRL09177880233697"]', ['GB30TWRL09177880233697'], 'invalid']]]
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 ["XX82WEST12345698765432"]invalidmalformedFailed
regression ["US64SVBKUS6S3300958879"]validmalformedFailed
control ["DE59811903138375291124"]validvalidPassed
control ["DE50296496123032354476"]validvalidPassed
control ["DE58862288567935243525"]validvalidPassed
control ["GB12YUBO17171961366336"]validvalidPassed
control ["GB30TWRL09177880233695"]validvalidPassed
control ["GB23HJGZ79203524065161"]validvalidPassed

SHA-256 / 70342cdf6b86ba194c218a2ced35099dba6971b54e1c7e3a92846e75f6d45bf8

3 / The verified repair

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

N = 1
observations = []
def solve(s):
    LENGTHS = {'DE': 22, 'GB': 22, 'FR': 27, 'NL': 18, 'BE': 16, 'NO': 15, 'MT': 31}
    t = s.replace(' ', '').upper()
    if len(t) < 5 or not t.isascii() or not t.isalnum():
        return 'malformed'
    if not t[:2].isalpha() or not t[2:4].isdigit() or not 2 <= int(t[2:4]) <= 98:
        return 'malformed'
    if LENGTHS.get(t[:2]) != len(t):
        return 'malformed'
    r = t[4:] + t[:4]
    n = ''.join(str(int(ch, 36)) for ch in r)
    return 'valid' if int(n) % 97 == 1 else 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['control ["DE59811903138375291124"]', ['DE59811903138375291124'], 'valid'], ['control ["DE50296496123032354476"]', ['DE50296496123032354476'], 'valid'], ['control ["DE58862288567935243525"]', ['DE58862288567935243525'], 'valid'], ['control ["GB12YUBO17171961366336"]', ['GB12YUBO17171961366336'], 'valid'], ['control ["GB30TWRL09177880233695"]', ['GB30TWRL09177880233695'], 'valid'], ['control ["GB23HJGZ79203524065161"]', ['GB23HJGZ79203524065161'], 'valid']], [['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['control ["GB23HJGZ79203524065161"]', ['GB23HJGZ79203524065161'], 'valid'], ['control ["FR88HIO0QY01NRB0VCRB14QO450"]', ['FR88HIO0QY01NRB0VCRB14QO450'], 'valid'], ['control ["FR38Q7OJKEPW4X8D3YP3O6RO9CF"]', ['FR38Q7OJKEPW4X8D3YP3O6RO9CF'], 'valid'], ['control ["FR87B5LPN0GWM8FRHG96JYM18V0"]', ['FR87B5LPN0GWM8FRHG96JYM18V0'], 'valid'], ['control ["NL34HYUK1800961826"]', ['NL34HYUK1800961826'], 'valid'], ['control ["NL15KCEZ0926524218"]', ['NL15KCEZ0926524218'], 'valid']], [['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['control ["NL15KCEZ0926524218"]', ['NL15KCEZ0926524218'], 'valid'], ['control ["NL97OZQU5858488442"]', ['NL97OZQU5858488442'], 'valid'], ['control ["BE73177155519075"]', ['BE73177155519075'], 'valid'], ['control ["BE31287393626938"]', ['BE31287393626938'], 'valid'], ['control ["BE87587165649240"]', ['BE87587165649240'], 'valid'], ['control ["NO5861733992430"]', ['NO5861733992430'], 'valid']], [['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['control ["NO5861733992430"]', ['NO5861733992430'], 'valid'], ['control ["NO7438063392154"]', ['NO7438063392154'], 'valid'], ['control ["NO8414946034784"]', ['NO8414946034784'], 'valid'], ['control ["MT48QBGB31246251447244909393188"]', ['MT48QBGB31246251447244909393188'], 'valid'], ['control ["MT24EODH27387088780936299640502"]', ['MT24EODH27387088780936299640502'], 'valid'], ['control ["MT98JHCG63199638448964680432116"]', ['MT98JHCG63199638448964680432116'], 'valid']], [['regression ["XX82WEST12345698765432"]', ['XX82WEST12345698765432'], 'malformed'], ['regression ["US64SVBKUS6S3300958879"]', ['US64SVBKUS6S3300958879'], 'malformed'], ['control ["MT98JHCG63199638448964680432116"]', ['MT98JHCG63199638448964680432116'], 'valid'], ['control ["DE59811903138375291127"]', ['DE59811903138375291127'], 'invalid'], ['control ["DE50296496123032354477"]', ['DE50296496123032354477'], 'invalid'], ['control ["DE58862288567935243527"]', ['DE58862288567935243527'], 'invalid'], ['control ["GB12YUBO17171961366337"]', ['GB12YUBO17171961366337'], 'invalid'], ['control ["GB30TWRL09177880233697"]', ['GB30TWRL09177880233697'], 'invalid']]]
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 ["XX82WEST12345698765432"]malformedmalformedPassed
regression ["US64SVBKUS6S3300958879"]malformedmalformedPassed
control ["DE59811903138375291124"]validvalidPassed
control ["DE50296496123032354476"]validvalidPassed
control ["DE58862288567935243525"]validvalidPassed
control ["GB12YUBO17171961366336"]validvalidPassed
control ["GB30TWRL09177880233695"]validvalidPassed
control ["GB23HJGZ79203524065161"]validvalidPassed

SHA-256 / 082c6b949cf43d70d2de1cd58ef7712685189bcf3ed0994f87bca85e0ad804b9

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:38.961304+00:00.

Case digest / 9fc1286b7c74195ad344f8b73706f6adc6289e508a7fc014539f1be3d568c180