FA-96541 / Phone number formatting / Open access
National number type validator: overlong BB numbers pass · case 01
An eleven-digit BB number starting 9 is reported as mobile.
ROOT CAUSE
The BB length gate only rejects numbers shorter than ten digits.
VERIFIED REPAIR
Require exactly ten digits before classifying BB numbers.
Unsuccessful approach: Checking the length only on the fixed branch lets overlong mobiles through.
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 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| toll-free block | tollfree | tollfree | Passed |
| fixed line starting 6 | fixed | fixed | Passed |
| mobile | mobile | mobile | Passed |
| short mobile | invalid | invalid | Passed |
| fixed line cannot start 1 | invalid | invalid | Passed |
| fixed line starting 2 | fixed | fixed | Passed |
| nine-digit fixed line | fixed | fixed | Passed |
| ten-digit fixed line too long | invalid | invalid | Passed |
| eleven-digit BB mobile | mobile | invalid | Failed |
| BB mobile | mobile | mobile | Passed |
| BB fixed | fixed | fixed | Passed |
| CC cannot start 1 | invalid | invalid | Passed |
| CC starting 9 | fixed_or_mobile | fixed_or_mobile | Passed |
| embedded space | invalid | invalid | Passed |
SHA-256 / dff2a21387780a1933df7c0f1c9607644b213a7f345f450f4dc8208aac9660e2
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) == 9 else 'invalid'
if '2' <= nsn[0] <= '6' and len(nsn) in (8, 9):
return 'fixed'
return 'invalid'
if c == 'BB':
if nsn[0] == '9':
return 'mobile'
return 'fixed' if '3' <= nsn[0] <= '8' and len(nsn) == 10 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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| toll-free block | tollfree | tollfree | Passed |
| fixed line starting 6 | fixed | fixed | Passed |
| mobile | mobile | mobile | Passed |
| short mobile | invalid | invalid | Passed |
| fixed line cannot start 1 | invalid | invalid | Passed |
| fixed line starting 2 | fixed | fixed | Passed |
| nine-digit fixed line | fixed | fixed | Passed |
| ten-digit fixed line too long | invalid | invalid | Passed |
| eleven-digit BB mobile | mobile | invalid | Failed |
| BB mobile | mobile | mobile | Passed |
| BB fixed | fixed | fixed | Passed |
| CC cannot start 1 | invalid | invalid | Passed |
| CC starting 9 | fixed_or_mobile | fixed_or_mobile | Passed |
| embedded space | invalid | invalid | Passed |
SHA-256 / 129d1ddead4b1e697b7b0fb07bd016c57fb611636436fe46b0b76e604f9b48a7
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| toll-free block | tollfree | tollfree | Passed |
| fixed line starting 6 | fixed | fixed | Passed |
| mobile | mobile | mobile | Passed |
| short mobile | invalid | invalid | Passed |
| fixed line cannot start 1 | invalid | invalid | Passed |
| fixed line starting 2 | fixed | fixed | Passed |
| nine-digit fixed line | fixed | fixed | Passed |
| ten-digit fixed line too long | invalid | invalid | Passed |
| eleven-digit BB mobile | invalid | invalid | Passed |
| BB mobile | mobile | mobile | Passed |
| BB fixed | fixed | fixed | Passed |
| CC cannot start 1 | invalid | invalid | Passed |
| CC starting 9 | fixed_or_mobile | fixed_or_mobile | Passed |
| embedded space | invalid | invalid | Passed |
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.698456+00:00.
Case digest / ab361f504cfe757736bb092b7c16e974feeab6f2b21f0c34a0d667dad916cb8b