FAILURE MAP
← Case archive

FA-96531 / Phone number formatting / Open access

National number type validator: mobile numbers of any length are accepted · case 01

An eight-digit number starting 7 is reported as mobile.

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

ROOT CAUSE

The mobile branch returns without checking the length.

VERIFIED REPAIR

Require exactly nine digits for AA mobiles.

Unsuccessful approach: Allowing eight or nine digits borrows the fixed-line length rule.

Case contract

Input [national significant number, country]; non-digit input is invalid. AA: prefix 600 is toll-free with 9 digits; leading 7 is mobile with 9 digits; leading 2..6 is fixed with 8 or 9 digits. BB: exactly 10 digits, leading 9 mobile, leading 3..8 fixed. CC: 7 digits with a leading 2..9 is "fixed_or_mobile". Everything else is "invalid".

Why this case matters

Number type decides SMS eligibility, pricing and fraud rules; the checks overlap on leading digits, so order and bounds matter.

1 / The failure

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

N = 1
observations = []
def solve(x):
    nsn, c = x
    if not nsn.isdigit():
        return 'invalid'
    if c == 'AA':
        if nsn.startswith('600'):
            return 'tollfree' if len(nsn) == 9 else 'invalid'
        if nsn[0] == '7':
            return 'mobile'
        if '2' <= nsn[0] <= '6' and len(nsn) in (8, 9):
            return 'fixed'
        return 'invalid'
    if c == 'BB':
        if len(nsn) != 10:
            return 'invalid'
        if nsn[0] == '9':
            return 'mobile'
        return 'fixed' if '3' <= nsn[0] <= '8' else 'invalid'
    if c == 'CC':
        return 'fixed_or_mobile' if len(nsn) == 7 and nsn[0] in '23456789' else 'invalid'
    return 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['toll-free block', ['600123456', 'AA'], 'tollfree'], ['fixed line starting 6', ['61234567', 'AA'], 'fixed'], ['mobile', ['712345678', 'AA'], 'mobile'], ['short mobile', ['71234567', 'AA'], 'invalid'], ['fixed line cannot start 1', ['11234567', 'AA'], 'invalid'], ['fixed line starting 2', ['21234567', 'AA'], 'fixed'], ['nine-digit fixed line', ['312345678', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3123456789', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['91234567890', 'BB'], 'invalid'], ['BB mobile', ['9123456789', 'BB'], 'mobile'], ['BB fixed', ['4123456789', 'BB'], 'fixed'], ['CC cannot start 1', ['1123456', 'CC'], 'invalid'], ['CC starting 9', ['9123456', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0111', 'AA'], 'invalid']], [['toll-free block', ['600234567', 'AA'], 'tollfree'], ['fixed line starting 6', ['62345678', 'AA'], 'fixed'], ['mobile', ['723456789', 'AA'], 'mobile'], ['short mobile', ['72345678', 'AA'], 'invalid'], ['fixed line cannot start 1', ['12345678', 'AA'], 'invalid'], ['fixed line starting 2', ['22345678', 'AA'], 'fixed'], ['nine-digit fixed line', ['323456789', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3234567890', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['92345678901', 'BB'], 'invalid'], ['BB mobile', ['9234567890', 'BB'], 'mobile'], ['BB fixed', ['4234567890', 'BB'], 'fixed'], ['CC cannot start 1', ['1234567', 'CC'], 'invalid'], ['CC starting 9', ['9234567', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0122', 'AA'], 'invalid']], [['toll-free block', ['600345678', 'AA'], 'tollfree'], ['fixed line starting 6', ['63456789', 'AA'], 'fixed'], ['mobile', ['734567890', 'AA'], 'mobile'], ['short mobile', ['73456789', 'AA'], 'invalid'], ['fixed line cannot start 1', ['13456789', 'AA'], 'invalid'], ['fixed line starting 2', ['23456789', 'AA'], 'fixed'], ['nine-digit fixed line', ['334567890', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3345678901', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['93456789012', 'BB'], 'invalid'], ['BB mobile', ['9345678901', 'BB'], 'mobile'], ['BB fixed', ['4345678901', 'BB'], 'fixed'], ['CC cannot start 1', ['1345678', 'CC'], 'invalid'], ['CC starting 9', ['9345678', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0133', 'AA'], 'invalid']], [['toll-free block', ['600456789', 'AA'], 'tollfree'], ['fixed line starting 6', ['64567890', 'AA'], 'fixed'], ['mobile', ['745678901', 'AA'], 'mobile'], ['short mobile', ['74567890', 'AA'], 'invalid'], ['fixed line cannot start 1', ['14567890', 'AA'], 'invalid'], ['fixed line starting 2', ['24567890', 'AA'], 'fixed'], ['nine-digit fixed line', ['345678901', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3456789012', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['94567890123', 'BB'], 'invalid'], ['BB mobile', ['9456789012', 'BB'], 'mobile'], ['BB fixed', ['4456789012', 'BB'], 'fixed'], ['CC cannot start 1', ['1456789', 'CC'], 'invalid'], ['CC starting 9', ['9456789', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0144', 'AA'], 'invalid']], [['toll-free block', ['600567890', 'AA'], 'tollfree'], ['fixed line starting 6', ['65678901', 'AA'], 'fixed'], ['mobile', ['756789012', 'AA'], 'mobile'], ['short mobile', ['75678901', 'AA'], 'invalid'], ['fixed line cannot start 1', ['15678901', 'AA'], 'invalid'], ['fixed line starting 2', ['25678901', 'AA'], 'fixed'], ['nine-digit fixed line', ['356789012', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3567890123', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['95678901234', 'BB'], 'invalid'], ['BB mobile', ['9567890123', 'BB'], 'mobile'], ['BB fixed', ['4567890123', 'BB'], 'fixed'], ['CC cannot start 1', ['1567890', 'CC'], 'invalid'], ['CC starting 9', ['9567890', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0155', 'AA'], 'invalid']]]
for label, arg, expected in fixtures[N - 1]:
    check(label, solve(arg), 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
toll-free blocktollfreetollfreePassed
fixed line starting 6fixedfixedPassed
mobilemobilemobilePassed
short mobilemobileinvalidFailed
fixed line cannot start 1invalidinvalidPassed
fixed line starting 2fixedfixedPassed
nine-digit fixed linefixedfixedPassed
ten-digit fixed line too longinvalidinvalidPassed
eleven-digit BB mobileinvalidinvalidPassed
BB mobilemobilemobilePassed
BB fixedfixedfixedPassed
CC cannot start 1invalidinvalidPassed
CC starting 9fixed_or_mobilefixed_or_mobilePassed
embedded spaceinvalidinvalidPassed

SHA-256 / d7091a87048352633b6539282a6bee3af5f3a6d651278645dff369a5f2965976

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    nsn, c = x
    if not nsn.isdigit():
        return 'invalid'
    if c == 'AA':
        if nsn.startswith('600'):
            return 'tollfree' if len(nsn) == 9 else 'invalid'
        if nsn[0] == '7':
            return 'mobile' if len(nsn) in (8, 9) else 'invalid'
        if '2' <= nsn[0] <= '6' and len(nsn) in (8, 9):
            return 'fixed'
        return 'invalid'
    if c == 'BB':
        if len(nsn) != 10:
            return 'invalid'
        if nsn[0] == '9':
            return 'mobile'
        return 'fixed' if '3' <= nsn[0] <= '8' else 'invalid'
    if c == 'CC':
        return 'fixed_or_mobile' if len(nsn) == 7 and nsn[0] in '23456789' else 'invalid'
    return 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['toll-free block', ['600123456', 'AA'], 'tollfree'], ['fixed line starting 6', ['61234567', 'AA'], 'fixed'], ['mobile', ['712345678', 'AA'], 'mobile'], ['short mobile', ['71234567', 'AA'], 'invalid'], ['fixed line cannot start 1', ['11234567', 'AA'], 'invalid'], ['fixed line starting 2', ['21234567', 'AA'], 'fixed'], ['nine-digit fixed line', ['312345678', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3123456789', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['91234567890', 'BB'], 'invalid'], ['BB mobile', ['9123456789', 'BB'], 'mobile'], ['BB fixed', ['4123456789', 'BB'], 'fixed'], ['CC cannot start 1', ['1123456', 'CC'], 'invalid'], ['CC starting 9', ['9123456', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0111', 'AA'], 'invalid']], [['toll-free block', ['600234567', 'AA'], 'tollfree'], ['fixed line starting 6', ['62345678', 'AA'], 'fixed'], ['mobile', ['723456789', 'AA'], 'mobile'], ['short mobile', ['72345678', 'AA'], 'invalid'], ['fixed line cannot start 1', ['12345678', 'AA'], 'invalid'], ['fixed line starting 2', ['22345678', 'AA'], 'fixed'], ['nine-digit fixed line', ['323456789', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3234567890', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['92345678901', 'BB'], 'invalid'], ['BB mobile', ['9234567890', 'BB'], 'mobile'], ['BB fixed', ['4234567890', 'BB'], 'fixed'], ['CC cannot start 1', ['1234567', 'CC'], 'invalid'], ['CC starting 9', ['9234567', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0122', 'AA'], 'invalid']], [['toll-free block', ['600345678', 'AA'], 'tollfree'], ['fixed line starting 6', ['63456789', 'AA'], 'fixed'], ['mobile', ['734567890', 'AA'], 'mobile'], ['short mobile', ['73456789', 'AA'], 'invalid'], ['fixed line cannot start 1', ['13456789', 'AA'], 'invalid'], ['fixed line starting 2', ['23456789', 'AA'], 'fixed'], ['nine-digit fixed line', ['334567890', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3345678901', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['93456789012', 'BB'], 'invalid'], ['BB mobile', ['9345678901', 'BB'], 'mobile'], ['BB fixed', ['4345678901', 'BB'], 'fixed'], ['CC cannot start 1', ['1345678', 'CC'], 'invalid'], ['CC starting 9', ['9345678', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0133', 'AA'], 'invalid']], [['toll-free block', ['600456789', 'AA'], 'tollfree'], ['fixed line starting 6', ['64567890', 'AA'], 'fixed'], ['mobile', ['745678901', 'AA'], 'mobile'], ['short mobile', ['74567890', 'AA'], 'invalid'], ['fixed line cannot start 1', ['14567890', 'AA'], 'invalid'], ['fixed line starting 2', ['24567890', 'AA'], 'fixed'], ['nine-digit fixed line', ['345678901', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3456789012', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['94567890123', 'BB'], 'invalid'], ['BB mobile', ['9456789012', 'BB'], 'mobile'], ['BB fixed', ['4456789012', 'BB'], 'fixed'], ['CC cannot start 1', ['1456789', 'CC'], 'invalid'], ['CC starting 9', ['9456789', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0144', 'AA'], 'invalid']], [['toll-free block', ['600567890', 'AA'], 'tollfree'], ['fixed line starting 6', ['65678901', 'AA'], 'fixed'], ['mobile', ['756789012', 'AA'], 'mobile'], ['short mobile', ['75678901', 'AA'], 'invalid'], ['fixed line cannot start 1', ['15678901', 'AA'], 'invalid'], ['fixed line starting 2', ['25678901', 'AA'], 'fixed'], ['nine-digit fixed line', ['356789012', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3567890123', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['95678901234', 'BB'], 'invalid'], ['BB mobile', ['9567890123', 'BB'], 'mobile'], ['BB fixed', ['4567890123', 'BB'], 'fixed'], ['CC cannot start 1', ['1567890', 'CC'], 'invalid'], ['CC starting 9', ['9567890', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0155', 'AA'], 'invalid']]]
for label, arg, expected in fixtures[N - 1]:
    check(label, solve(arg), 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
toll-free blocktollfreetollfreePassed
fixed line starting 6fixedfixedPassed
mobilemobilemobilePassed
short mobilemobileinvalidFailed
fixed line cannot start 1invalidinvalidPassed
fixed line starting 2fixedfixedPassed
nine-digit fixed linefixedfixedPassed
ten-digit fixed line too longinvalidinvalidPassed
eleven-digit BB mobileinvalidinvalidPassed
BB mobilemobilemobilePassed
BB fixedfixedfixedPassed
CC cannot start 1invalidinvalidPassed
CC starting 9fixed_or_mobilefixed_or_mobilePassed
embedded spaceinvalidinvalidPassed

SHA-256 / 5a086ea57d7b78c0a436503eeef89725d16e07a3bf9b0e89989a9ee13a60c295

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    nsn, c = x
    if not nsn.isdigit():
        return 'invalid'
    if c == 'AA':
        if nsn.startswith('600'):
            return 'tollfree' if len(nsn) == 9 else 'invalid'
        if nsn[0] == '7':
            return 'mobile' if len(nsn) == 9 else 'invalid'
        if '2' <= nsn[0] <= '6' and len(nsn) in (8, 9):
            return 'fixed'
        return 'invalid'
    if c == 'BB':
        if len(nsn) != 10:
            return 'invalid'
        if nsn[0] == '9':
            return 'mobile'
        return 'fixed' if '3' <= nsn[0] <= '8' else 'invalid'
    if c == 'CC':
        return 'fixed_or_mobile' if len(nsn) == 7 and nsn[0] in '23456789' else 'invalid'
    return 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['toll-free block', ['600123456', 'AA'], 'tollfree'], ['fixed line starting 6', ['61234567', 'AA'], 'fixed'], ['mobile', ['712345678', 'AA'], 'mobile'], ['short mobile', ['71234567', 'AA'], 'invalid'], ['fixed line cannot start 1', ['11234567', 'AA'], 'invalid'], ['fixed line starting 2', ['21234567', 'AA'], 'fixed'], ['nine-digit fixed line', ['312345678', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3123456789', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['91234567890', 'BB'], 'invalid'], ['BB mobile', ['9123456789', 'BB'], 'mobile'], ['BB fixed', ['4123456789', 'BB'], 'fixed'], ['CC cannot start 1', ['1123456', 'CC'], 'invalid'], ['CC starting 9', ['9123456', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0111', 'AA'], 'invalid']], [['toll-free block', ['600234567', 'AA'], 'tollfree'], ['fixed line starting 6', ['62345678', 'AA'], 'fixed'], ['mobile', ['723456789', 'AA'], 'mobile'], ['short mobile', ['72345678', 'AA'], 'invalid'], ['fixed line cannot start 1', ['12345678', 'AA'], 'invalid'], ['fixed line starting 2', ['22345678', 'AA'], 'fixed'], ['nine-digit fixed line', ['323456789', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3234567890', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['92345678901', 'BB'], 'invalid'], ['BB mobile', ['9234567890', 'BB'], 'mobile'], ['BB fixed', ['4234567890', 'BB'], 'fixed'], ['CC cannot start 1', ['1234567', 'CC'], 'invalid'], ['CC starting 9', ['9234567', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0122', 'AA'], 'invalid']], [['toll-free block', ['600345678', 'AA'], 'tollfree'], ['fixed line starting 6', ['63456789', 'AA'], 'fixed'], ['mobile', ['734567890', 'AA'], 'mobile'], ['short mobile', ['73456789', 'AA'], 'invalid'], ['fixed line cannot start 1', ['13456789', 'AA'], 'invalid'], ['fixed line starting 2', ['23456789', 'AA'], 'fixed'], ['nine-digit fixed line', ['334567890', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3345678901', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['93456789012', 'BB'], 'invalid'], ['BB mobile', ['9345678901', 'BB'], 'mobile'], ['BB fixed', ['4345678901', 'BB'], 'fixed'], ['CC cannot start 1', ['1345678', 'CC'], 'invalid'], ['CC starting 9', ['9345678', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0133', 'AA'], 'invalid']], [['toll-free block', ['600456789', 'AA'], 'tollfree'], ['fixed line starting 6', ['64567890', 'AA'], 'fixed'], ['mobile', ['745678901', 'AA'], 'mobile'], ['short mobile', ['74567890', 'AA'], 'invalid'], ['fixed line cannot start 1', ['14567890', 'AA'], 'invalid'], ['fixed line starting 2', ['24567890', 'AA'], 'fixed'], ['nine-digit fixed line', ['345678901', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3456789012', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['94567890123', 'BB'], 'invalid'], ['BB mobile', ['9456789012', 'BB'], 'mobile'], ['BB fixed', ['4456789012', 'BB'], 'fixed'], ['CC cannot start 1', ['1456789', 'CC'], 'invalid'], ['CC starting 9', ['9456789', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0144', 'AA'], 'invalid']], [['toll-free block', ['600567890', 'AA'], 'tollfree'], ['fixed line starting 6', ['65678901', 'AA'], 'fixed'], ['mobile', ['756789012', 'AA'], 'mobile'], ['short mobile', ['75678901', 'AA'], 'invalid'], ['fixed line cannot start 1', ['15678901', 'AA'], 'invalid'], ['fixed line starting 2', ['25678901', 'AA'], 'fixed'], ['nine-digit fixed line', ['356789012', 'AA'], 'fixed'], ['ten-digit fixed line too long', ['3567890123', 'AA'], 'invalid'], ['eleven-digit BB mobile', ['95678901234', 'BB'], 'invalid'], ['BB mobile', ['9567890123', 'BB'], 'mobile'], ['BB fixed', ['4567890123', 'BB'], 'fixed'], ['CC cannot start 1', ['1567890', 'CC'], 'invalid'], ['CC starting 9', ['9567890', 'CC'], 'fixed_or_mobile'], ['embedded space', ['5550 0155', 'AA'], 'invalid']]]
for label, arg, expected in fixtures[N - 1]:
    check(label, solve(arg), 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
toll-free blocktollfreetollfreePassed
fixed line starting 6fixedfixedPassed
mobilemobilemobilePassed
short mobileinvalidinvalidPassed
fixed line cannot start 1invalidinvalidPassed
fixed line starting 2fixedfixedPassed
nine-digit fixed linefixedfixedPassed
ten-digit fixed line too longinvalidinvalidPassed
eleven-digit BB mobileinvalidinvalidPassed
BB mobilemobilemobilePassed
BB fixedfixedfixedPassed
CC cannot start 1invalidinvalidPassed
CC starting 9fixed_or_mobilefixed_or_mobilePassed
embedded spaceinvalidinvalidPassed

SHA-256 / 08d5ef294579ec7d61b93233eac703261e82b8d692548ebbaed45c1947a7ec10

Verification & scope

A deterministic toy contract over synthetic, obviously fictitious telephone numbers and toy country tables. The rules are stipulated for this model only and make no claim of conformance with any numbering plan or telecommunications standard. 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:52:23.664951+00:00.

Case digest / 7acdaad2852c3563ce6d1d26ac49240710ca93a7c39b0d9e4efa372ad95c4650