FAILURE MAP
← Case archive

FA-72436 / Check-digit algorithms / Open access

ISBN-13 conversion keeps the ISBN-10 check character · case 01

Converted numbers end with the old mod-11 check (sometimes X) instead of an EAN check digit.

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

ROOT CAUSE

The body copies all ten ISBN-10 characters and drops the new computation.

VERIFIED REPAIR

Discard the ISBN-10 check character, prefix 978, and compute a new mod-10 check digit.

Unsuccessful approach: Recomputing the check but over the stale 13-character string still includes the old check character.

Case contract

Convert a hyphenated or plain ISBN-10 to ISBN-13. The input must be a valid ISBN-10 (mod 11, X allowed as the final character) or None is returned. The result is "978" + the nine ISBN-10 payload digits + a freshly computed EAN-13 check digit (weights 1,3 alternating from the left).

Why this case matters

Library and retail systems migrate legacy ISBN-10 records to 13-digit EAN identifiers.

1 / The failure

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

N = 1
observations = []
def solve(s):
    t = s.replace('-', '')
    if len(t) != 10 or not t.isascii() or not t[:9].isdigit() or not (t[9].isdigit() or t[9] == 'X'):
        return None
    if sum((10 - i) * (10 if ch == 'X' else int(ch)) for i, ch in enumerate(t)) % 11:
        return None
    body = '978' + t[:9]
    total = sum(int(ch) * (1 if i % 2 == 0 else 3) for i, ch in enumerate(body))
    return '978' + t
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5892872939"]', ['5892872939'], '9785892872935'], ['regression ["2399553950"]', ['2399553950'], '9782399553954'], ['partial-repair ["9465545449"]', ['9465545449'], '9789465545448'], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None]], [['regression ["829284466X"]', ['829284466X'], '9788292844663'], ['regression ["9379774826"]', ['9379774826'], '9789379774828'], ['partial-repair ["2266685686"]', ['2266685686'], '9782266685689'], ['partial-repair ["5900307905"]', ['5900307905'], '9785900307909'], ['control ["2266685687"]', ['2266685687'], None], ['control ["0306406153"]', ['0306406153'], None], ['control ["12345"]', ['12345'], None], ['control ["5892872930"]', ['5892872930'], None]], [['regression ["2892681790"]', ['2892681790'], '9782892681796'], ['regression ["5900307905"]', ['5900307905'], '9785900307909'], ['partial-repair ["7113753523"]', ['7113753523'], '9787113753528'], ['partial-repair ["1475433131"]', ['1475433131'], '9781475433135'], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None], ['control ["2266685687"]', ['2266685687'], None]], [['regression ["7113753523"]', ['7113753523'], '9787113753528'], ['regression ["1475433131"]', ['1475433131'], '9781475433135'], ['partial-repair ["8449627923"]', ['8449627923'], '9788449627927'], ['partial-repair ["0436178273"]', ['0436178273'], '9780436178276'], ['control ["12345"]', ['12345'], None], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None]], [['regression ["8449627923"]', ['8449627923'], '9788449627927'], ['regression ["0436178273"]', ['0436178273'], '9780436178276'], ['partial-repair ["9999999999"]', ['9999999999'], '9789999999991'], ['partial-repair ["0-306-40615-2"]', ['0-306-40615-2'], '9780306406157'], ['control ["9379774827"]', ['9379774827'], None], ['control ["2266685687"]', ['2266685687'], None], ['control ["0306406153"]', ['0306406153'], None], ['control ["12345"]', ['12345'], None]]]
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 ["5892872939"]97858928729399785892872935Failed
regression ["2399553950"]97823995539509782399553954Failed
partial-repair ["9465545449"]97894655454499789465545448Failed
control ["5892872930"]NoneNonePassed
control ["2399553951"]NoneNonePassed
control ["9465545440"]NoneNonePassed
control ["8292844660"]NoneNonePassed
control ["9379774827"]NoneNonePassed

SHA-256 / 80f812254edf16da8f2ebb9a234744a2c0367a041b40a10a14c85831115d5fe4

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('-', '')
    if len(t) != 10 or not t.isascii() or not t[:9].isdigit() or not (t[9].isdigit() or t[9] == 'X'):
        return None
    if sum((10 - i) * (10 if ch == 'X' else int(ch)) for i, ch in enumerate(t)) % 11:
        return None
    body = '978' + t[:9] + ('0' if t[9] == 'X' else t[9])
    total = sum(int(ch) * (1 if i % 2 == 0 else 3) for i, ch in enumerate(body))
    return body[:12] + str((10 - total % 10) % 10)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5892872939"]', ['5892872939'], '9785892872935'], ['regression ["2399553950"]', ['2399553950'], '9782399553954'], ['partial-repair ["9465545449"]', ['9465545449'], '9789465545448'], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None]], [['regression ["829284466X"]', ['829284466X'], '9788292844663'], ['regression ["9379774826"]', ['9379774826'], '9789379774828'], ['partial-repair ["2266685686"]', ['2266685686'], '9782266685689'], ['partial-repair ["5900307905"]', ['5900307905'], '9785900307909'], ['control ["2266685687"]', ['2266685687'], None], ['control ["0306406153"]', ['0306406153'], None], ['control ["12345"]', ['12345'], None], ['control ["5892872930"]', ['5892872930'], None]], [['regression ["2892681790"]', ['2892681790'], '9782892681796'], ['regression ["5900307905"]', ['5900307905'], '9785900307909'], ['partial-repair ["7113753523"]', ['7113753523'], '9787113753528'], ['partial-repair ["1475433131"]', ['1475433131'], '9781475433135'], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None], ['control ["2266685687"]', ['2266685687'], None]], [['regression ["7113753523"]', ['7113753523'], '9787113753528'], ['regression ["1475433131"]', ['1475433131'], '9781475433135'], ['partial-repair ["8449627923"]', ['8449627923'], '9788449627927'], ['partial-repair ["0436178273"]', ['0436178273'], '9780436178276'], ['control ["12345"]', ['12345'], None], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None]], [['regression ["8449627923"]', ['8449627923'], '9788449627927'], ['regression ["0436178273"]', ['0436178273'], '9780436178276'], ['partial-repair ["9999999999"]', ['9999999999'], '9789999999991'], ['partial-repair ["0-306-40615-2"]', ['0-306-40615-2'], '9780306406157'], ['control ["9379774827"]', ['9379774827'], None], ['control ["2266685687"]', ['2266685687'], None], ['control ["0306406153"]', ['0306406153'], None], ['control ["12345"]', ['12345'], None]]]
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 ["5892872939"]97858928729369785892872935Failed
regression ["2399553950"]97823995539549782399553954Passed
partial-repair ["9465545449"]97894655454499789465545448Failed
control ["5892872930"]NoneNonePassed
control ["2399553951"]NoneNonePassed
control ["9465545440"]NoneNonePassed
control ["8292844660"]NoneNonePassed
control ["9379774827"]NoneNonePassed

SHA-256 / 4761199bc5c9223b7abe469e80371c24685a06d69c96e76f36f3e2dd81cd3373

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('-', '')
    if len(t) != 10 or not t.isascii() or not t[:9].isdigit() or not (t[9].isdigit() or t[9] == 'X'):
        return None
    if sum((10 - i) * (10 if ch == 'X' else int(ch)) for i, ch in enumerate(t)) % 11:
        return None
    body = '978' + t[:9]
    total = sum(int(ch) * (1 if i % 2 == 0 else 3) for i, ch in enumerate(body))
    return body + str((10 - total % 10) % 10)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression ["5892872939"]', ['5892872939'], '9785892872935'], ['regression ["2399553950"]', ['2399553950'], '9782399553954'], ['partial-repair ["9465545449"]', ['9465545449'], '9789465545448'], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None]], [['regression ["829284466X"]', ['829284466X'], '9788292844663'], ['regression ["9379774826"]', ['9379774826'], '9789379774828'], ['partial-repair ["2266685686"]', ['2266685686'], '9782266685689'], ['partial-repair ["5900307905"]', ['5900307905'], '9785900307909'], ['control ["2266685687"]', ['2266685687'], None], ['control ["0306406153"]', ['0306406153'], None], ['control ["12345"]', ['12345'], None], ['control ["5892872930"]', ['5892872930'], None]], [['regression ["2892681790"]', ['2892681790'], '9782892681796'], ['regression ["5900307905"]', ['5900307905'], '9785900307909'], ['partial-repair ["7113753523"]', ['7113753523'], '9787113753528'], ['partial-repair ["1475433131"]', ['1475433131'], '9781475433135'], ['control ["9465545440"]', ['9465545440'], None], ['control ["8292844660"]', ['8292844660'], None], ['control ["9379774827"]', ['9379774827'], None], ['control ["2266685687"]', ['2266685687'], None]], [['regression ["7113753523"]', ['7113753523'], '9787113753528'], ['regression ["1475433131"]', ['1475433131'], '9781475433135'], ['partial-repair ["8449627923"]', ['8449627923'], '9788449627927'], ['partial-repair ["0436178273"]', ['0436178273'], '9780436178276'], ['control ["12345"]', ['12345'], None], ['control ["5892872930"]', ['5892872930'], None], ['control ["2399553951"]', ['2399553951'], None], ['control ["9465545440"]', ['9465545440'], None]], [['regression ["8449627923"]', ['8449627923'], '9788449627927'], ['regression ["0436178273"]', ['0436178273'], '9780436178276'], ['partial-repair ["9999999999"]', ['9999999999'], '9789999999991'], ['partial-repair ["0-306-40615-2"]', ['0-306-40615-2'], '9780306406157'], ['control ["9379774827"]', ['9379774827'], None], ['control ["2266685687"]', ['2266685687'], None], ['control ["0306406153"]', ['0306406153'], None], ['control ["12345"]', ['12345'], None]]]
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 ["5892872939"]97858928729359785892872935Passed
regression ["2399553950"]97823995539549782399553954Passed
partial-repair ["9465545449"]97894655454489789465545448Passed
control ["5892872930"]NoneNonePassed
control ["2399553951"]NoneNonePassed
control ["9465545440"]NoneNonePassed
control ["8292844660"]NoneNonePassed
control ["9379774827"]NoneNonePassed

SHA-256 / db8deb8d8064cea06914ef236c52ef43960e593b793fc9c58b5aaee33f753632

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

Case digest / 16f0bc60a81ee735e9a8593835d938c93977368058004a339cc249f9650f9d45