FA-72426 / Check-digit algorithms / Open access
ISBN-10 validates the weighted sum modulo ten · case 01
X-terminated and many digit-terminated ISBNs are judged incorrectly.
ROOT CAUSE
The weighted sum is tested with % 10 instead of % 11.
VERIFIED REPAIR
ISBN-10 is a mod-11 scheme; test total % 11 == 0.
Unsuccessful approach: Folding the mod-11 remainder through % 10 also accepts sums with remainder 10.
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('-', '').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 % 10 == 0 else 'invalid'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5892872939"]', ['5892872939'], 'valid'], ['regression ["2399553950"]', ['2399553950'], 'valid'], ['partial-repair ["5892872938"]', ['5892872938'], 'invalid'], ['partial-repair ["9465545448"]', ['9465545448'], 'invalid'], ['control ["0000000000"]', ['0000000000'], 'valid'], ['control ["123456789X"]', ['123456789X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid'], ['control ["2399553951"]', ['2399553951'], 'invalid']], [['regression ["829284466X"]', ['829284466X'], 'valid'], ['regression ["9379774826"]', ['9379774826'], 'valid'], ['partial-repair ["9379774825"]', ['9379774825'], 'invalid'], ['partial-repair ["2266685685"]', ['2266685685'], 'invalid'], ['control ["8292844660"]', ['8292844660'], 'invalid'], ['control ["9379774827"]', ['9379774827'], 'invalid'], ['control ["2266685687"]', ['2266685687'], 'invalid'], ['control ["2892681791"]', ['2892681791'], 'invalid']], [['regression ["2892681790"]', ['2892681790'], 'valid'], ['regression ["5900307905"]', ['5900307905'], 'valid'], ['partial-repair ["5892872938"]', ['5892872938'], 'invalid'], ['partial-repair ["9465545448"]', ['9465545448'], 'invalid'], ['control ["99921-58-10-7"]', ['99921-58-10-7'], 'valid'], ['control ["03064061"]', ['03064061'], 'malformed'], ['control ["030640615X1"]', ['030640615X1'], 'malformed'], ['control ["0306406X52"]', ['0306406X52'], 'malformed']], [['regression ["7113753523"]', ['7113753523'], 'valid'], ['regression ["1475433131"]', ['1475433131'], 'valid'], ['partial-repair ["9379774825"]', ['9379774825'], 'invalid'], ['partial-repair ["2266685685"]', ['2266685685'], 'invalid'], ['control ["0000000000"]', ['0000000000'], 'valid'], ['control ["123456789X"]', ['123456789X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid'], ['control ["2399553951"]', ['2399553951'], 'invalid']], [['regression ["8449627923"]', ['8449627923'], 'valid'], ['regression ["0436178273"]', ['0436178273'], 'valid'], ['partial-repair ["5892872938"]', ['5892872938'], 'invalid'], ['partial-repair ["9465545448"]', ['9465545448'], 'invalid'], ['control ["8292844660"]', ['8292844660'], 'invalid'], ['control ["9379774827"]', ['9379774827'], 'invalid'], ['control ["2266685687"]', ['2266685687'], 'invalid'], ['control ["2892681791"]', ['2892681791'], '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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression ["5892872939"] | invalid | valid | Failed |
| regression ["2399553950"] | invalid | valid | Failed |
| partial-repair ["5892872938"] | valid | invalid | Failed |
| partial-repair ["9465545448"] | invalid | invalid | Passed |
| control ["0000000000"] | valid | valid | Passed |
| control ["123456789X"] | valid | valid | Passed |
| control ["5892872930"] | invalid | invalid | Passed |
| control ["2399553951"] | invalid | invalid | Passed |
SHA-256 / cbf1c644354d519bf83b8fbcc863c0d7017e90e1cae7647f836a3beca95e230a
2 / The unsuccessful fix
Exit 1"""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 % 10 == 0 else 'invalid'
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5892872939"]', ['5892872939'], 'valid'], ['regression ["2399553950"]', ['2399553950'], 'valid'], ['partial-repair ["5892872938"]', ['5892872938'], 'invalid'], ['partial-repair ["9465545448"]', ['9465545448'], 'invalid'], ['control ["0000000000"]', ['0000000000'], 'valid'], ['control ["123456789X"]', ['123456789X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid'], ['control ["2399553951"]', ['2399553951'], 'invalid']], [['regression ["829284466X"]', ['829284466X'], 'valid'], ['regression ["9379774826"]', ['9379774826'], 'valid'], ['partial-repair ["9379774825"]', ['9379774825'], 'invalid'], ['partial-repair ["2266685685"]', ['2266685685'], 'invalid'], ['control ["8292844660"]', ['8292844660'], 'invalid'], ['control ["9379774827"]', ['9379774827'], 'invalid'], ['control ["2266685687"]', ['2266685687'], 'invalid'], ['control ["2892681791"]', ['2892681791'], 'invalid']], [['regression ["2892681790"]', ['2892681790'], 'valid'], ['regression ["5900307905"]', ['5900307905'], 'valid'], ['partial-repair ["5892872938"]', ['5892872938'], 'invalid'], ['partial-repair ["9465545448"]', ['9465545448'], 'invalid'], ['control ["99921-58-10-7"]', ['99921-58-10-7'], 'valid'], ['control ["03064061"]', ['03064061'], 'malformed'], ['control ["030640615X1"]', ['030640615X1'], 'malformed'], ['control ["0306406X52"]', ['0306406X52'], 'malformed']], [['regression ["7113753523"]', ['7113753523'], 'valid'], ['regression ["1475433131"]', ['1475433131'], 'valid'], ['partial-repair ["9379774825"]', ['9379774825'], 'invalid'], ['partial-repair ["2266685685"]', ['2266685685'], 'invalid'], ['control ["0000000000"]', ['0000000000'], 'valid'], ['control ["123456789X"]', ['123456789X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid'], ['control ["2399553951"]', ['2399553951'], 'invalid']], [['regression ["8449627923"]', ['8449627923'], 'valid'], ['regression ["0436178273"]', ['0436178273'], 'valid'], ['partial-repair ["5892872938"]', ['5892872938'], 'invalid'], ['partial-repair ["9465545448"]', ['9465545448'], 'invalid'], ['control ["8292844660"]', ['8292844660'], 'invalid'], ['control ["9379774827"]', ['9379774827'], 'invalid'], ['control ["2266685687"]', ['2266685687'], 'invalid'], ['control ["2892681791"]', ['2892681791'], '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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression ["5892872939"] | valid | valid | Passed |
| regression ["2399553950"] | valid | valid | Passed |
| partial-repair ["5892872938"] | valid | invalid | Failed |
| partial-repair ["9465545448"] | valid | invalid | Failed |
| control ["0000000000"] | valid | valid | Passed |
| control ["123456789X"] | valid | valid | Passed |
| control ["5892872930"] | invalid | invalid | Passed |
| control ["2399553951"] | invalid | invalid | Passed |
SHA-256 / 9f0a3e530aa7341cea2fd9c2df1692691a3fd92fe9cb96789c4c024be920c2d0
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 ["5892872939"]', ['5892872939'], 'valid'], ['regression ["2399553950"]', ['2399553950'], 'valid'], ['partial-repair ["5892872938"]', ['5892872938'], 'invalid'], ['partial-repair ["9465545448"]', ['9465545448'], 'invalid'], ['control ["0000000000"]', ['0000000000'], 'valid'], ['control ["123456789X"]', ['123456789X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid'], ['control ["2399553951"]', ['2399553951'], 'invalid']], [['regression ["829284466X"]', ['829284466X'], 'valid'], ['regression ["9379774826"]', ['9379774826'], 'valid'], ['partial-repair ["9379774825"]', ['9379774825'], 'invalid'], ['partial-repair ["2266685685"]', ['2266685685'], 'invalid'], ['control ["8292844660"]', ['8292844660'], 'invalid'], ['control ["9379774827"]', ['9379774827'], 'invalid'], ['control ["2266685687"]', ['2266685687'], 'invalid'], ['control ["2892681791"]', ['2892681791'], 'invalid']], [['regression ["2892681790"]', ['2892681790'], 'valid'], ['regression ["5900307905"]', ['5900307905'], 'valid'], ['partial-repair ["5892872938"]', ['5892872938'], 'invalid'], ['partial-repair ["9465545448"]', ['9465545448'], 'invalid'], ['control ["99921-58-10-7"]', ['99921-58-10-7'], 'valid'], ['control ["03064061"]', ['03064061'], 'malformed'], ['control ["030640615X1"]', ['030640615X1'], 'malformed'], ['control ["0306406X52"]', ['0306406X52'], 'malformed']], [['regression ["7113753523"]', ['7113753523'], 'valid'], ['regression ["1475433131"]', ['1475433131'], 'valid'], ['partial-repair ["9379774825"]', ['9379774825'], 'invalid'], ['partial-repair ["2266685685"]', ['2266685685'], 'invalid'], ['control ["0000000000"]', ['0000000000'], 'valid'], ['control ["123456789X"]', ['123456789X'], 'valid'], ['control ["5892872930"]', ['5892872930'], 'invalid'], ['control ["2399553951"]', ['2399553951'], 'invalid']], [['regression ["8449627923"]', ['8449627923'], 'valid'], ['regression ["0436178273"]', ['0436178273'], 'valid'], ['partial-repair ["5892872938"]', ['5892872938'], 'invalid'], ['partial-repair ["9465545448"]', ['9465545448'], 'invalid'], ['control ["8292844660"]', ['8292844660'], 'invalid'], ['control ["9379774827"]', ['9379774827'], 'invalid'], ['control ["2266685687"]', ['2266685687'], 'invalid'], ['control ["2892681791"]', ['2892681791'], '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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression ["5892872939"] | valid | valid | Passed |
| regression ["2399553950"] | valid | valid | Passed |
| partial-repair ["5892872938"] | invalid | invalid | Passed |
| partial-repair ["9465545448"] | invalid | invalid | Passed |
| control ["0000000000"] | valid | valid | Passed |
| control ["123456789X"] | valid | valid | Passed |
| control ["5892872930"] | invalid | invalid | Passed |
| control ["2399553951"] | invalid | invalid | Passed |
SHA-256 / 5ee45dd275ecc7161ebedd6207c8c41d261febce4b1240e5f4dd969b5878b5bb
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.429524+00:00.
Case digest / 360919c1f7440aa9f69032d296a80862dbb62b353e1d7a9312917c0ab87cdf8f