FAILURE MAP
← Case archive

FA-72431 / Check-digit algorithms / Open access

ISBN-10 removes only the first hyphen · case 01

Hyphenated ISBNs are reported malformed.

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

ROOT CAUSE

The normalizer calls replace with a count of 1, leaving later group hyphens in place.

VERIFIED REPAIR

Remove every hyphen (and space) before the length check.

Unsuccessful approach: Stripping hyphens only at the ends leaves the interior group separators.

Case contract

ISBN-10 validation. Hyphens and spaces are removed; the rest must be nine ASCII digits followed by a digit or uppercase X (value 10), else "malformed". Weights run 10 down to 1 from the left and the number is "valid" when the weighted sum is divisible by 11, else "invalid".

Why this case matters

Catalogue and bookstore systems validate legacy ISBN-10 identifiers on intake.

1 / The failure

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

N = 1
observations = []
def solve(s):
    t = s.replace('-', '', 1).replace(' ', '')
    if len(t) != 10 or not t.isascii() or not t[:9].isdigit():
        return 'malformed'
    if not (t[9].isdigit() or t[9] == 'X'):
        return 'malformed'
    total = 0
    for i, ch in enumerate(t):
        v = 10 if ch == 'X' else int(ch)
        total += (10 - i) * v
    return 'valid' if total % 11 == 0 else 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5-892-87293-9"]', ['5-892-87293-9'], 'valid'], ['regression ["2-399-55395-0"]', ['2-399-55395-0'], 'valid'], ['control ["5892872939"]', ['5892872939'], 'valid'], ['control ["2399553950"]', ['2399553950'], 'valid'], ['control ["9465545449"]', ['9465545449'], 'valid'], ['control ["829284466X"]', ['829284466X'], 'valid'], ['control ["9379774826"]', ['9379774826'], 'valid'], ['control ["2266685686"]', ['2266685686'], 'valid']], [['regression ["8-292-84466-X"]', ['8-292-84466-X'], 'valid'], ['regression ["9-379-77482-6"]', ['9-379-77482-6'], 'valid'], ['control ["2266685686"]', ['2266685686'], 'valid'], ['control ["2892681790"]', ['2892681790'], 'valid'], ['control ["5900307905"]', ['5900307905'], 'valid'], ['control ["3905987139"]', ['3905987139'], 'valid'], ['control ["7113753523"]', ['7113753523'], 'valid'], ['control ["1475433131"]', ['1475433131'], 'valid']], [['regression ["0-306-40615-2"]', ['0-306-40615-2'], 'valid'], ['regression ["0-8044-2957-X"]', ['0-8044-2957-X'], 'valid'], ['control ["1475433131"]', ['1475433131'], 'valid'], ['control ["3407375115"]', ['3407375115'], 'valid'], ['control ["8449627923"]', ['8449627923'], 'valid'], ['control ["0436178273"]', ['0436178273'], 'valid'], ['control ["080442957X"]', ['080442957X'], 'valid'], ['control ["0306406152"]', ['0306406152'], 'valid']], [['regression ["1-84356-028-3"]', ['1-84356-028-3'], 'valid'], ['regression ["5-892-87293-9"]', ['5-892-87293-9'], 'valid'], ['control ["0306406152"]', ['0306406152'], 'valid'], ['control ["9999999999"]', ['9999999999'], 'valid'], ['control ["0000000000"]', ['0000000000'], 'valid'], ['control ["123456789X"]', ['123456789X'], 'valid'], ['control ["097522980X"]', ['097522980X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid']], [['regression ["9-465-54544-9"]', ['9-465-54544-9'], 'valid'], ['regression ["8-292-84466-X"]', ['8-292-84466-X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid'], ['control ["2399553951"]', ['2399553951'], 'invalid'], ['control ["9465545440"]', ['9465545440'], 'invalid'], ['control ["8292844660"]', ['8292844660'], 'invalid'], ['control ["9379774827"]', ['9379774827'], 'invalid'], ['control ["2266685687"]', ['2266685687'], '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 ["5-892-87293-9"]malformedvalidFailed
regression ["2-399-55395-0"]malformedvalidFailed
control ["5892872939"]validvalidPassed
control ["2399553950"]validvalidPassed
control ["9465545449"]validvalidPassed
control ["829284466X"]validvalidPassed
control ["9379774826"]validvalidPassed
control ["2266685686"]validvalidPassed

SHA-256 / 00a9b3f05e9b05dd647efd63aba8aec62ee6cab0b7aaedc0d5c98e95b8756ff8

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(s):
    t = s.strip('-').replace(' ', '')
    if len(t) != 10 or not t.isascii() or not t[:9].isdigit():
        return 'malformed'
    if not (t[9].isdigit() or t[9] == 'X'):
        return 'malformed'
    total = 0
    for i, ch in enumerate(t):
        v = 10 if ch == 'X' else int(ch)
        total += (10 - i) * v
    return 'valid' if total % 11 == 0 else 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5-892-87293-9"]', ['5-892-87293-9'], 'valid'], ['regression ["2-399-55395-0"]', ['2-399-55395-0'], 'valid'], ['control ["5892872939"]', ['5892872939'], 'valid'], ['control ["2399553950"]', ['2399553950'], 'valid'], ['control ["9465545449"]', ['9465545449'], 'valid'], ['control ["829284466X"]', ['829284466X'], 'valid'], ['control ["9379774826"]', ['9379774826'], 'valid'], ['control ["2266685686"]', ['2266685686'], 'valid']], [['regression ["8-292-84466-X"]', ['8-292-84466-X'], 'valid'], ['regression ["9-379-77482-6"]', ['9-379-77482-6'], 'valid'], ['control ["2266685686"]', ['2266685686'], 'valid'], ['control ["2892681790"]', ['2892681790'], 'valid'], ['control ["5900307905"]', ['5900307905'], 'valid'], ['control ["3905987139"]', ['3905987139'], 'valid'], ['control ["7113753523"]', ['7113753523'], 'valid'], ['control ["1475433131"]', ['1475433131'], 'valid']], [['regression ["0-306-40615-2"]', ['0-306-40615-2'], 'valid'], ['regression ["0-8044-2957-X"]', ['0-8044-2957-X'], 'valid'], ['control ["1475433131"]', ['1475433131'], 'valid'], ['control ["3407375115"]', ['3407375115'], 'valid'], ['control ["8449627923"]', ['8449627923'], 'valid'], ['control ["0436178273"]', ['0436178273'], 'valid'], ['control ["080442957X"]', ['080442957X'], 'valid'], ['control ["0306406152"]', ['0306406152'], 'valid']], [['regression ["1-84356-028-3"]', ['1-84356-028-3'], 'valid'], ['regression ["5-892-87293-9"]', ['5-892-87293-9'], 'valid'], ['control ["0306406152"]', ['0306406152'], 'valid'], ['control ["9999999999"]', ['9999999999'], 'valid'], ['control ["0000000000"]', ['0000000000'], 'valid'], ['control ["123456789X"]', ['123456789X'], 'valid'], ['control ["097522980X"]', ['097522980X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid']], [['regression ["9-465-54544-9"]', ['9-465-54544-9'], 'valid'], ['regression ["8-292-84466-X"]', ['8-292-84466-X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid'], ['control ["2399553951"]', ['2399553951'], 'invalid'], ['control ["9465545440"]', ['9465545440'], 'invalid'], ['control ["8292844660"]', ['8292844660'], 'invalid'], ['control ["9379774827"]', ['9379774827'], 'invalid'], ['control ["2266685687"]', ['2266685687'], '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 ["5-892-87293-9"]malformedvalidFailed
regression ["2-399-55395-0"]malformedvalidFailed
control ["5892872939"]validvalidPassed
control ["2399553950"]validvalidPassed
control ["9465545449"]validvalidPassed
control ["829284466X"]validvalidPassed
control ["9379774826"]validvalidPassed
control ["2266685686"]validvalidPassed

SHA-256 / 4008c2d32622615472eda70ca446573e63a363fbf0e210dc1c8b4b6d5c9ad194

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('-', '').replace(' ', '')
    if len(t) != 10 or not t.isascii() or not t[:9].isdigit():
        return 'malformed'
    if not (t[9].isdigit() or t[9] == 'X'):
        return 'malformed'
    total = 0
    for i, ch in enumerate(t):
        v = 10 if ch == 'X' else int(ch)
        total += (10 - i) * v
    return 'valid' if total % 11 == 0 else 'invalid'
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5-892-87293-9"]', ['5-892-87293-9'], 'valid'], ['regression ["2-399-55395-0"]', ['2-399-55395-0'], 'valid'], ['control ["5892872939"]', ['5892872939'], 'valid'], ['control ["2399553950"]', ['2399553950'], 'valid'], ['control ["9465545449"]', ['9465545449'], 'valid'], ['control ["829284466X"]', ['829284466X'], 'valid'], ['control ["9379774826"]', ['9379774826'], 'valid'], ['control ["2266685686"]', ['2266685686'], 'valid']], [['regression ["8-292-84466-X"]', ['8-292-84466-X'], 'valid'], ['regression ["9-379-77482-6"]', ['9-379-77482-6'], 'valid'], ['control ["2266685686"]', ['2266685686'], 'valid'], ['control ["2892681790"]', ['2892681790'], 'valid'], ['control ["5900307905"]', ['5900307905'], 'valid'], ['control ["3905987139"]', ['3905987139'], 'valid'], ['control ["7113753523"]', ['7113753523'], 'valid'], ['control ["1475433131"]', ['1475433131'], 'valid']], [['regression ["0-306-40615-2"]', ['0-306-40615-2'], 'valid'], ['regression ["0-8044-2957-X"]', ['0-8044-2957-X'], 'valid'], ['control ["1475433131"]', ['1475433131'], 'valid'], ['control ["3407375115"]', ['3407375115'], 'valid'], ['control ["8449627923"]', ['8449627923'], 'valid'], ['control ["0436178273"]', ['0436178273'], 'valid'], ['control ["080442957X"]', ['080442957X'], 'valid'], ['control ["0306406152"]', ['0306406152'], 'valid']], [['regression ["1-84356-028-3"]', ['1-84356-028-3'], 'valid'], ['regression ["5-892-87293-9"]', ['5-892-87293-9'], 'valid'], ['control ["0306406152"]', ['0306406152'], 'valid'], ['control ["9999999999"]', ['9999999999'], 'valid'], ['control ["0000000000"]', ['0000000000'], 'valid'], ['control ["123456789X"]', ['123456789X'], 'valid'], ['control ["097522980X"]', ['097522980X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid']], [['regression ["9-465-54544-9"]', ['9-465-54544-9'], 'valid'], ['regression ["8-292-84466-X"]', ['8-292-84466-X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid'], ['control ["2399553951"]', ['2399553951'], 'invalid'], ['control ["9465545440"]', ['9465545440'], 'invalid'], ['control ["8292844660"]', ['8292844660'], 'invalid'], ['control ["9379774827"]', ['9379774827'], 'invalid'], ['control ["2266685687"]', ['2266685687'], '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 ["5-892-87293-9"]validvalidPassed
regression ["2-399-55395-0"]validvalidPassed
control ["5892872939"]validvalidPassed
control ["2399553950"]validvalidPassed
control ["9465545449"]validvalidPassed
control ["829284466X"]validvalidPassed
control ["9379774826"]validvalidPassed
control ["2266685686"]validvalidPassed

SHA-256 / add9c14cbef68efff951d56804337b48857801df936b84983e0899e2ed7edd38

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

Case digest / 6ed7f118668e790598dd5ca559e7d5db1a4c6ac92a89aa23a821a26647b56f9d