FAILURE MAP
← Case archive

FA-96416 / Phone number formatting / Open access

Variable-length area code grouping: mobile block code taken as three digits · case 01

Mobile numbers print as "07xx xxx xxx".

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

ROOT CAUSE

The mobile block code is sliced as three digits.

VERIFIED REPAIR

Use a four-digit block code for mobile numbers.

Unsuccessful approach: Detecting mobile numbers by "07" after the trunk was already removed never detects them.

Case contract

Keep digits and drop one leading trunk 0; nine national digits must remain. Numbers starting 7 are mobile with a four-digit block code; otherwise the area code is the longest match from 2, 30, 31, 40, 4012 (no match -> None). The subscriber part is one group if at most 5 digits, two equal halves if even, else 3 then the rest. Return "0" + area + " " + groups joined by spaces.

Why this case matters

Area codes of different lengths share leading digits; choosing the wrong one regroups every digit and prints a number users cannot dial.

1 / The failure

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

N = 1
observations = []
def solve(x):
    AREAS = ['2', '30', '31', '40', '4012']
    d = ''.join(c for c in x if c.isdigit())
    if d.startswith('0'):
        d = d[1:]
    if len(d) != 9:
        return None
    if d[0] == '7':
        area = d[:3]
    else:
        area = max((a for a in AREAS if d.startswith(a)), key=len, default=None)
        if area is None:
            return None
    sub = d[len(area):]
    if len(sub) <= 5:
        groups = [sub]
    elif len(sub) % 2 == 0:
        h = len(sub) // 2
        groups = [sub[:h], sub[h:]]
    else:
        groups = [sub[:3], sub[3:]]
    return '0' + area + ' ' + ' '.join(groups)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['one-digit area', '02 55501234', '02 5550 1234'], ['two-digit area', '030 5501234', '030 550 1234'], ['long area code wins over its prefix', '04012 01234', '04012 01234'], ['short area code', '040-3501234', '040 350 1234'], ['mobile block', '07111 01234', '07111 01234'], ['ten digits without trunk', '1255501234', None], ['unknown area prefix', '0955501234', None], ['national number typed without trunk', '255501234', '02 5550 1234']], [['one-digit area', '02 55502345', '02 5550 2345'], ['two-digit area', '030 5502345', '030 550 2345'], ['long area code wins over its prefix', '04012 02345', '04012 02345'], ['short area code', '040-3502345', '040 350 2345'], ['mobile block', '07222 02345', '07222 02345'], ['ten digits without trunk', '1255502345', None], ['unknown area prefix', '0955502345', None], ['national number typed without trunk', '255502345', '02 5550 2345']], [['one-digit area', '02 55503456', '02 5550 3456'], ['two-digit area', '030 5503456', '030 550 3456'], ['long area code wins over its prefix', '04012 03456', '04012 03456'], ['short area code', '040-3503456', '040 350 3456'], ['mobile block', '07333 03456', '07333 03456'], ['ten digits without trunk', '1255503456', None], ['unknown area prefix', '0955503456', None], ['national number typed without trunk', '255503456', '02 5550 3456']], [['one-digit area', '02 55504567', '02 5550 4567'], ['two-digit area', '030 5504567', '030 550 4567'], ['long area code wins over its prefix', '04012 04567', '04012 04567'], ['short area code', '040-3504567', '040 350 4567'], ['mobile block', '07444 04567', '07444 04567'], ['ten digits without trunk', '1255504567', None], ['unknown area prefix', '0955504567', None], ['national number typed without trunk', '255504567', '02 5550 4567']], [['one-digit area', '02 55505678', '02 5550 5678'], ['two-digit area', '030 5505678', '030 550 5678'], ['long area code wins over its prefix', '04012 05678', '04012 05678'], ['short area code', '040-3505678', '040 350 5678'], ['mobile block', '07555 05678', '07555 05678'], ['ten digits without trunk', '1255505678', None], ['unknown area prefix', '0955505678', None], ['national number typed without trunk', '255505678', '02 5550 5678']]]
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
one-digit area02 5550 123402 5550 1234Passed
two-digit area030 550 1234030 550 1234Passed
long area code wins over its prefix04012 0123404012 01234Passed
short area code040 350 1234040 350 1234Passed
mobile block0711 101 23407111 01234Failed
ten digits without trunkNoneNonePassed
unknown area prefixNoneNonePassed
national number typed without trunk02 5550 123402 5550 1234Passed

SHA-256 / 53b13325c9c50c3c2132d2b0f3eb10a0462eeeeacd4c41677df0d0bdebe9d1d1

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(x):
    AREAS = ['2', '30', '31', '40', '4012']
    d = ''.join(c for c in x if c.isdigit())
    if d.startswith('0'):
        d = d[1:]
    if len(d) != 9:
        return None
    if d.startswith('07'):
        area = d[:4]
    else:
        area = max((a for a in AREAS if d.startswith(a)), key=len, default=None)
        if area is None:
            return None
    sub = d[len(area):]
    if len(sub) <= 5:
        groups = [sub]
    elif len(sub) % 2 == 0:
        h = len(sub) // 2
        groups = [sub[:h], sub[h:]]
    else:
        groups = [sub[:3], sub[3:]]
    return '0' + area + ' ' + ' '.join(groups)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['one-digit area', '02 55501234', '02 5550 1234'], ['two-digit area', '030 5501234', '030 550 1234'], ['long area code wins over its prefix', '04012 01234', '04012 01234'], ['short area code', '040-3501234', '040 350 1234'], ['mobile block', '07111 01234', '07111 01234'], ['ten digits without trunk', '1255501234', None], ['unknown area prefix', '0955501234', None], ['national number typed without trunk', '255501234', '02 5550 1234']], [['one-digit area', '02 55502345', '02 5550 2345'], ['two-digit area', '030 5502345', '030 550 2345'], ['long area code wins over its prefix', '04012 02345', '04012 02345'], ['short area code', '040-3502345', '040 350 2345'], ['mobile block', '07222 02345', '07222 02345'], ['ten digits without trunk', '1255502345', None], ['unknown area prefix', '0955502345', None], ['national number typed without trunk', '255502345', '02 5550 2345']], [['one-digit area', '02 55503456', '02 5550 3456'], ['two-digit area', '030 5503456', '030 550 3456'], ['long area code wins over its prefix', '04012 03456', '04012 03456'], ['short area code', '040-3503456', '040 350 3456'], ['mobile block', '07333 03456', '07333 03456'], ['ten digits without trunk', '1255503456', None], ['unknown area prefix', '0955503456', None], ['national number typed without trunk', '255503456', '02 5550 3456']], [['one-digit area', '02 55504567', '02 5550 4567'], ['two-digit area', '030 5504567', '030 550 4567'], ['long area code wins over its prefix', '04012 04567', '04012 04567'], ['short area code', '040-3504567', '040 350 4567'], ['mobile block', '07444 04567', '07444 04567'], ['ten digits without trunk', '1255504567', None], ['unknown area prefix', '0955504567', None], ['national number typed without trunk', '255504567', '02 5550 4567']], [['one-digit area', '02 55505678', '02 5550 5678'], ['two-digit area', '030 5505678', '030 550 5678'], ['long area code wins over its prefix', '04012 05678', '04012 05678'], ['short area code', '040-3505678', '040 350 5678'], ['mobile block', '07555 05678', '07555 05678'], ['ten digits without trunk', '1255505678', None], ['unknown area prefix', '0955505678', None], ['national number typed without trunk', '255505678', '02 5550 5678']]]
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
one-digit area02 5550 123402 5550 1234Passed
two-digit area030 550 1234030 550 1234Passed
long area code wins over its prefix04012 0123404012 01234Passed
short area code040 350 1234040 350 1234Passed
mobile blockNone07111 01234Failed
ten digits without trunkNoneNonePassed
unknown area prefixNoneNonePassed
national number typed without trunk02 5550 123402 5550 1234Passed

SHA-256 / 04b0973193fd1573e05f7e6367f23224ad62be6791cef0cb389613216b84570e

3 / The verified repair

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

N = 1
observations = []
def solve(x):
    AREAS = ['2', '30', '31', '40', '4012']
    d = ''.join(c for c in x if c.isdigit())
    if d.startswith('0'):
        d = d[1:]
    if len(d) != 9:
        return None
    if d[0] == '7':
        area = d[:4]
    else:
        area = max((a for a in AREAS if d.startswith(a)), key=len, default=None)
        if area is None:
            return None
    sub = d[len(area):]
    if len(sub) <= 5:
        groups = [sub]
    elif len(sub) % 2 == 0:
        h = len(sub) // 2
        groups = [sub[:h], sub[h:]]
    else:
        groups = [sub[:3], sub[3:]]
    return '0' + area + ' ' + ' '.join(groups)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['one-digit area', '02 55501234', '02 5550 1234'], ['two-digit area', '030 5501234', '030 550 1234'], ['long area code wins over its prefix', '04012 01234', '04012 01234'], ['short area code', '040-3501234', '040 350 1234'], ['mobile block', '07111 01234', '07111 01234'], ['ten digits without trunk', '1255501234', None], ['unknown area prefix', '0955501234', None], ['national number typed without trunk', '255501234', '02 5550 1234']], [['one-digit area', '02 55502345', '02 5550 2345'], ['two-digit area', '030 5502345', '030 550 2345'], ['long area code wins over its prefix', '04012 02345', '04012 02345'], ['short area code', '040-3502345', '040 350 2345'], ['mobile block', '07222 02345', '07222 02345'], ['ten digits without trunk', '1255502345', None], ['unknown area prefix', '0955502345', None], ['national number typed without trunk', '255502345', '02 5550 2345']], [['one-digit area', '02 55503456', '02 5550 3456'], ['two-digit area', '030 5503456', '030 550 3456'], ['long area code wins over its prefix', '04012 03456', '04012 03456'], ['short area code', '040-3503456', '040 350 3456'], ['mobile block', '07333 03456', '07333 03456'], ['ten digits without trunk', '1255503456', None], ['unknown area prefix', '0955503456', None], ['national number typed without trunk', '255503456', '02 5550 3456']], [['one-digit area', '02 55504567', '02 5550 4567'], ['two-digit area', '030 5504567', '030 550 4567'], ['long area code wins over its prefix', '04012 04567', '04012 04567'], ['short area code', '040-3504567', '040 350 4567'], ['mobile block', '07444 04567', '07444 04567'], ['ten digits without trunk', '1255504567', None], ['unknown area prefix', '0955504567', None], ['national number typed without trunk', '255504567', '02 5550 4567']], [['one-digit area', '02 55505678', '02 5550 5678'], ['two-digit area', '030 5505678', '030 550 5678'], ['long area code wins over its prefix', '04012 05678', '04012 05678'], ['short area code', '040-3505678', '040 350 5678'], ['mobile block', '07555 05678', '07555 05678'], ['ten digits without trunk', '1255505678', None], ['unknown area prefix', '0955505678', None], ['national number typed without trunk', '255505678', '02 5550 5678']]]
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
one-digit area02 5550 123402 5550 1234Passed
two-digit area030 550 1234030 550 1234Passed
long area code wins over its prefix04012 0123404012 01234Passed
short area code040 350 1234040 350 1234Passed
mobile block07111 0123407111 01234Passed
ten digits without trunkNoneNonePassed
unknown area prefixNoneNonePassed
national number typed without trunk02 5550 123402 5550 1234Passed

SHA-256 / 830693da487542d60a53ef955bc8d823ef571b2d54e416f514f1b9bbbd5d5009

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

Case digest / a7bded3f967eb84ca1a038a41aa6931ca39e3bb64f24a3e2d74594e9cab307f6