FAILURE MAP
← Case archive

FA-72031 / Error-correcting codes / Open access

RAID-5 parity rotates in the right-asymmetric direction · case 01

Rebuilt stripes return parity as data for most stripes.

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

ROOT CAUSE

The parity disk is computed as s mod n instead of n-1-(s mod n).

VERIFIED REPAIR

Place parity on disk n-1-(s mod n) for the left-symmetric layout.

Unsuccessful approach: Using (n - s) mod n is one disk off.

Case contract

RAID-5 left-symmetric layout on n >= 3 disks: in stripe s the parity lives on disk n-1-(s mod n) and data chunk j lives on disk (parity + 1 + j) mod n. blocks lists the stripe per disk (ints; at most one None for a failed disk). A missing block is rebuilt by XOR; with no missing block the XOR of all blocks must be 0 or the result is "parity-mismatch". Return [data chunks in logical order, parity block].

Why this case matters

Array controllers rebuild degraded stripes and must map disks to logical chunks correctly.

1 / The failure

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

N = 1
observations = []
def solve(blocks, stripe, n):
    if n < 3 or len(blocks) != n or blocks.count(None) > 1:
        return None
    pd = stripe % n
    if None in blocks:
        m = blocks.index(None)
        x = 0
        for k, v in enumerate(blocks):
            if k != m:
                x ^= v
        blocks = blocks[:m] + [x] + blocks[m + 1:]
    else:
        x = 0
        for v in blocks:
            x ^= v
        if x != 0:
            return 'parity-mismatch'
    order = [(pd + 1 + j) % n for j in range(n - 1)]
    return [[blocks[d] for d in order], blocks[pd]]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression [[132, 23, 147], 0, 3]', [[132, 23, 147], 0, 3], [[132, 23], 147]], ['regression [[132, null, 147], 0, 3]', [[132, None, 147], 0, 3], [[132, 23], 147]], ['control [[133, 23, 147], 0, 3]', [[133, 23, 147], 0, 3], 'parity-mismatch'], ['control [[81, 29, 77], 1, 3]', [[81, 29, 77], 1, 3], 'parity-mismatch'], ['control [[167, 226, 235, 175], 0, 4]', [[167, 226, 235, 175], 0, 4], 'parity-mismatch'], ['control [[196, 130, 1, 70], 1, 4]', [[196, 130, 1, 70], 1, 4], 'parity-mismatch'], ['control [[203, 146, 183, 239], 2, 4]', [[203, 146, 183, 239], 2, 4], 'parity-mismatch'], ['control [[97, 103, 54, 49], 3, 4]', [[97, 103, 54, 49], 3, 4], 'parity-mismatch']], [['regression [[166, null, 235, 175], 0, 4]', [[166, None, 235, 175], 0, 4], [[166, 226, 235], 175]], ['regression [[197, 130, 1, 70], 1, 4]', [[197, 130, 1, 70], 1, 4], [[70, 197, 130], 1]], ['partial-repair [[80, null, 77], 1, 3]', [[80, None, 77], 1, 3], [[77, 80], 29]], ['partial-repair [[166, 226, 235, 175], 0, 4]', [[166, 226, 235, 175], 0, 4], [[166, 226, 235], 175]], ['control [[97, 103, 54, 49], 3, 4]', [[97, 103, 54, 49], 3, 4], 'parity-mismatch'], ['control [[44, 55, 172, 192, 118], 2, 5]', [[44, 55, 172, 192, 118], 2, 5], 'parity-mismatch'], ['control [[94, 206, 36, 83, 230], 6, 5]', [[94, 206, 36, 83, 230], 6, 5], 'parity-mismatch'], ['control [[49, 163, 83, 128, 142, 206], 4, 6]', [[49, 163, 83, 128, 142, 206], 4, 6], 'parity-mismatch']], [['regression [[202, 146, 183, 239], 2, 4]', [[202, 146, 183, 239], 2, 4], [[183, 239, 202], 146]], ['regression [[null, 146, 183, 239], 2, 4]', [[None, 146, 183, 239], 2, 4], [[183, 239, 202], 146]], ['partial-repair [[197, 130, 1, 70], 1, 4]', [[197, 130, 1, 70], 1, 4], [[70, 197, 130], 1]], ['partial-repair [[197, null, 1, 70], 1, 4]', [[197, None, 1, 70], 1, 4], [[70, 197, 130], 1]], ['control [[18, 127, 108], 2, 3]', [[18, 127, 108], 2, 3], 'parity-mismatch'], ['control [[134, 159, 201, 163, 114], 0, 5]', [[134, 159, 201, 163, 114], 0, 5], 'parity-mismatch'], ['control [[1, null, null], 0, 3]', [[1, None, None], 0, 3], None], ['control [[1, 2], 0, 2]', [[1, 2], 0, 2], None]], [['regression [[96, 103, null, 49], 3, 4]', [[96, 103, None, 49], 3, 4], [[103, 54, 49], 96]], ['regression [[95, 206, 36, 83, 230], 6, 5]', [[95, 206, 36, 83, 230], 6, 5], [[230, 95, 206, 36], 83]], ['partial-repair [[null, 146, 183, 239], 2, 4]', [[None, 146, 183, 239], 2, 4], [[183, 239, 202], 146]], ['partial-repair [[96, 103, 54, 49], 3, 4]', [[96, 103, 54, 49], 3, 4], [[103, 54, 49], 96]], ['control [[133, 23, 147], 0, 3]', [[133, 23, 147], 0, 3], 'parity-mismatch'], ['control [[81, 29, 77], 1, 3]', [[81, 29, 77], 1, 3], 'parity-mismatch'], ['control [[167, 226, 235, 175], 0, 4]', [[167, 226, 235, 175], 0, 4], 'parity-mismatch'], ['control [[196, 130, 1, 70], 1, 4]', [[196, 130, 1, 70], 1, 4], 'parity-mismatch']], [['regression [[48, 163, 83, 128, 142, 206], 4, 6]', [[48, 163, 83, 128, 142, 206], 4, 6], [[83, 128, 142, 206, 48], 163]], ['regression [[null, 163, 83, 128, 142, 206], 4, 6]', [[None, 163, 83, 128, 142, 206], 4, 6], [[83, 128, 142, 206, 48], 163]], ['partial-repair [[45, 55, 172, 192, 118], 2, 5]', [[45, 55, 172, 192, 118], 2, 5], [[192, 118, 45, 55], 172]], ['partial-repair [[45, 55, null, 192, 118], 2, 5]', [[45, 55, None, 192, 118], 2, 5], [[192, 118, 45, 55], 172]], ['control [[97, 103, 54, 49], 3, 4]', [[97, 103, 54, 49], 3, 4], 'parity-mismatch'], ['control [[44, 55, 172, 192, 118], 2, 5]', [[44, 55, 172, 192, 118], 2, 5], 'parity-mismatch'], ['control [[94, 206, 36, 83, 230], 6, 5]', [[94, 206, 36, 83, 230], 6, 5], 'parity-mismatch'], ['control [[49, 163, 83, 128, 142, 206], 4, 6]', [[49, 163, 83, 128, 142, 206], 4, 6], 'parity-mismatch']]]
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 [[132, 23, 147], 0, 3][[23, 147], 132][[132, 23], 147]Failed
regression [[132, null, 147], 0, 3][[23, 147], 132][[132, 23], 147]Failed
control [[133, 23, 147], 0, 3]parity-mismatchparity-mismatchPassed
control [[81, 29, 77], 1, 3]parity-mismatchparity-mismatchPassed
control [[167, 226, 235, 175], 0, 4]parity-mismatchparity-mismatchPassed
control [[196, 130, 1, 70], 1, 4]parity-mismatchparity-mismatchPassed
control [[203, 146, 183, 239], 2, 4]parity-mismatchparity-mismatchPassed
control [[97, 103, 54, 49], 3, 4]parity-mismatchparity-mismatchPassed

SHA-256 / fadfa63a91285e1412488c8578150f34838f66c0be7e73275d40be8215a157b1

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(blocks, stripe, n):
    if n < 3 or len(blocks) != n or blocks.count(None) > 1:
        return None
    pd = (n - stripe) % n
    if None in blocks:
        m = blocks.index(None)
        x = 0
        for k, v in enumerate(blocks):
            if k != m:
                x ^= v
        blocks = blocks[:m] + [x] + blocks[m + 1:]
    else:
        x = 0
        for v in blocks:
            x ^= v
        if x != 0:
            return 'parity-mismatch'
    order = [(pd + 1 + j) % n for j in range(n - 1)]
    return [[blocks[d] for d in order], blocks[pd]]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression [[132, 23, 147], 0, 3]', [[132, 23, 147], 0, 3], [[132, 23], 147]], ['regression [[132, null, 147], 0, 3]', [[132, None, 147], 0, 3], [[132, 23], 147]], ['control [[133, 23, 147], 0, 3]', [[133, 23, 147], 0, 3], 'parity-mismatch'], ['control [[81, 29, 77], 1, 3]', [[81, 29, 77], 1, 3], 'parity-mismatch'], ['control [[167, 226, 235, 175], 0, 4]', [[167, 226, 235, 175], 0, 4], 'parity-mismatch'], ['control [[196, 130, 1, 70], 1, 4]', [[196, 130, 1, 70], 1, 4], 'parity-mismatch'], ['control [[203, 146, 183, 239], 2, 4]', [[203, 146, 183, 239], 2, 4], 'parity-mismatch'], ['control [[97, 103, 54, 49], 3, 4]', [[97, 103, 54, 49], 3, 4], 'parity-mismatch']], [['regression [[166, null, 235, 175], 0, 4]', [[166, None, 235, 175], 0, 4], [[166, 226, 235], 175]], ['regression [[197, 130, 1, 70], 1, 4]', [[197, 130, 1, 70], 1, 4], [[70, 197, 130], 1]], ['partial-repair [[80, null, 77], 1, 3]', [[80, None, 77], 1, 3], [[77, 80], 29]], ['partial-repair [[166, 226, 235, 175], 0, 4]', [[166, 226, 235, 175], 0, 4], [[166, 226, 235], 175]], ['control [[97, 103, 54, 49], 3, 4]', [[97, 103, 54, 49], 3, 4], 'parity-mismatch'], ['control [[44, 55, 172, 192, 118], 2, 5]', [[44, 55, 172, 192, 118], 2, 5], 'parity-mismatch'], ['control [[94, 206, 36, 83, 230], 6, 5]', [[94, 206, 36, 83, 230], 6, 5], 'parity-mismatch'], ['control [[49, 163, 83, 128, 142, 206], 4, 6]', [[49, 163, 83, 128, 142, 206], 4, 6], 'parity-mismatch']], [['regression [[202, 146, 183, 239], 2, 4]', [[202, 146, 183, 239], 2, 4], [[183, 239, 202], 146]], ['regression [[null, 146, 183, 239], 2, 4]', [[None, 146, 183, 239], 2, 4], [[183, 239, 202], 146]], ['partial-repair [[197, 130, 1, 70], 1, 4]', [[197, 130, 1, 70], 1, 4], [[70, 197, 130], 1]], ['partial-repair [[197, null, 1, 70], 1, 4]', [[197, None, 1, 70], 1, 4], [[70, 197, 130], 1]], ['control [[18, 127, 108], 2, 3]', [[18, 127, 108], 2, 3], 'parity-mismatch'], ['control [[134, 159, 201, 163, 114], 0, 5]', [[134, 159, 201, 163, 114], 0, 5], 'parity-mismatch'], ['control [[1, null, null], 0, 3]', [[1, None, None], 0, 3], None], ['control [[1, 2], 0, 2]', [[1, 2], 0, 2], None]], [['regression [[96, 103, null, 49], 3, 4]', [[96, 103, None, 49], 3, 4], [[103, 54, 49], 96]], ['regression [[95, 206, 36, 83, 230], 6, 5]', [[95, 206, 36, 83, 230], 6, 5], [[230, 95, 206, 36], 83]], ['partial-repair [[null, 146, 183, 239], 2, 4]', [[None, 146, 183, 239], 2, 4], [[183, 239, 202], 146]], ['partial-repair [[96, 103, 54, 49], 3, 4]', [[96, 103, 54, 49], 3, 4], [[103, 54, 49], 96]], ['control [[133, 23, 147], 0, 3]', [[133, 23, 147], 0, 3], 'parity-mismatch'], ['control [[81, 29, 77], 1, 3]', [[81, 29, 77], 1, 3], 'parity-mismatch'], ['control [[167, 226, 235, 175], 0, 4]', [[167, 226, 235, 175], 0, 4], 'parity-mismatch'], ['control [[196, 130, 1, 70], 1, 4]', [[196, 130, 1, 70], 1, 4], 'parity-mismatch']], [['regression [[48, 163, 83, 128, 142, 206], 4, 6]', [[48, 163, 83, 128, 142, 206], 4, 6], [[83, 128, 142, 206, 48], 163]], ['regression [[null, 163, 83, 128, 142, 206], 4, 6]', [[None, 163, 83, 128, 142, 206], 4, 6], [[83, 128, 142, 206, 48], 163]], ['partial-repair [[45, 55, 172, 192, 118], 2, 5]', [[45, 55, 172, 192, 118], 2, 5], [[192, 118, 45, 55], 172]], ['partial-repair [[45, 55, null, 192, 118], 2, 5]', [[45, 55, None, 192, 118], 2, 5], [[192, 118, 45, 55], 172]], ['control [[97, 103, 54, 49], 3, 4]', [[97, 103, 54, 49], 3, 4], 'parity-mismatch'], ['control [[44, 55, 172, 192, 118], 2, 5]', [[44, 55, 172, 192, 118], 2, 5], 'parity-mismatch'], ['control [[94, 206, 36, 83, 230], 6, 5]', [[94, 206, 36, 83, 230], 6, 5], 'parity-mismatch'], ['control [[49, 163, 83, 128, 142, 206], 4, 6]', [[49, 163, 83, 128, 142, 206], 4, 6], 'parity-mismatch']]]
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 [[132, 23, 147], 0, 3][[23, 147], 132][[132, 23], 147]Failed
regression [[132, null, 147], 0, 3][[23, 147], 132][[132, 23], 147]Failed
control [[133, 23, 147], 0, 3]parity-mismatchparity-mismatchPassed
control [[81, 29, 77], 1, 3]parity-mismatchparity-mismatchPassed
control [[167, 226, 235, 175], 0, 4]parity-mismatchparity-mismatchPassed
control [[196, 130, 1, 70], 1, 4]parity-mismatchparity-mismatchPassed
control [[203, 146, 183, 239], 2, 4]parity-mismatchparity-mismatchPassed
control [[97, 103, 54, 49], 3, 4]parity-mismatchparity-mismatchPassed

SHA-256 / 969cdb596b887e0bc4b7ada6f148d74c00d8c67ba45846a1eae40ed235f256fe

3 / The verified repair

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

N = 1
observations = []
def solve(blocks, stripe, n):
    if n < 3 or len(blocks) != n or blocks.count(None) > 1:
        return None
    pd = n - 1 - stripe % n
    if None in blocks:
        m = blocks.index(None)
        x = 0
        for k, v in enumerate(blocks):
            if k != m:
                x ^= v
        blocks = blocks[:m] + [x] + blocks[m + 1:]
    else:
        x = 0
        for v in blocks:
            x ^= v
        if x != 0:
            return 'parity-mismatch'
    order = [(pd + 1 + j) % n for j in range(n - 1)]
    return [[blocks[d] for d in order], blocks[pd]]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression [[132, 23, 147], 0, 3]', [[132, 23, 147], 0, 3], [[132, 23], 147]], ['regression [[132, null, 147], 0, 3]', [[132, None, 147], 0, 3], [[132, 23], 147]], ['control [[133, 23, 147], 0, 3]', [[133, 23, 147], 0, 3], 'parity-mismatch'], ['control [[81, 29, 77], 1, 3]', [[81, 29, 77], 1, 3], 'parity-mismatch'], ['control [[167, 226, 235, 175], 0, 4]', [[167, 226, 235, 175], 0, 4], 'parity-mismatch'], ['control [[196, 130, 1, 70], 1, 4]', [[196, 130, 1, 70], 1, 4], 'parity-mismatch'], ['control [[203, 146, 183, 239], 2, 4]', [[203, 146, 183, 239], 2, 4], 'parity-mismatch'], ['control [[97, 103, 54, 49], 3, 4]', [[97, 103, 54, 49], 3, 4], 'parity-mismatch']], [['regression [[166, null, 235, 175], 0, 4]', [[166, None, 235, 175], 0, 4], [[166, 226, 235], 175]], ['regression [[197, 130, 1, 70], 1, 4]', [[197, 130, 1, 70], 1, 4], [[70, 197, 130], 1]], ['partial-repair [[80, null, 77], 1, 3]', [[80, None, 77], 1, 3], [[77, 80], 29]], ['partial-repair [[166, 226, 235, 175], 0, 4]', [[166, 226, 235, 175], 0, 4], [[166, 226, 235], 175]], ['control [[97, 103, 54, 49], 3, 4]', [[97, 103, 54, 49], 3, 4], 'parity-mismatch'], ['control [[44, 55, 172, 192, 118], 2, 5]', [[44, 55, 172, 192, 118], 2, 5], 'parity-mismatch'], ['control [[94, 206, 36, 83, 230], 6, 5]', [[94, 206, 36, 83, 230], 6, 5], 'parity-mismatch'], ['control [[49, 163, 83, 128, 142, 206], 4, 6]', [[49, 163, 83, 128, 142, 206], 4, 6], 'parity-mismatch']], [['regression [[202, 146, 183, 239], 2, 4]', [[202, 146, 183, 239], 2, 4], [[183, 239, 202], 146]], ['regression [[null, 146, 183, 239], 2, 4]', [[None, 146, 183, 239], 2, 4], [[183, 239, 202], 146]], ['partial-repair [[197, 130, 1, 70], 1, 4]', [[197, 130, 1, 70], 1, 4], [[70, 197, 130], 1]], ['partial-repair [[197, null, 1, 70], 1, 4]', [[197, None, 1, 70], 1, 4], [[70, 197, 130], 1]], ['control [[18, 127, 108], 2, 3]', [[18, 127, 108], 2, 3], 'parity-mismatch'], ['control [[134, 159, 201, 163, 114], 0, 5]', [[134, 159, 201, 163, 114], 0, 5], 'parity-mismatch'], ['control [[1, null, null], 0, 3]', [[1, None, None], 0, 3], None], ['control [[1, 2], 0, 2]', [[1, 2], 0, 2], None]], [['regression [[96, 103, null, 49], 3, 4]', [[96, 103, None, 49], 3, 4], [[103, 54, 49], 96]], ['regression [[95, 206, 36, 83, 230], 6, 5]', [[95, 206, 36, 83, 230], 6, 5], [[230, 95, 206, 36], 83]], ['partial-repair [[null, 146, 183, 239], 2, 4]', [[None, 146, 183, 239], 2, 4], [[183, 239, 202], 146]], ['partial-repair [[96, 103, 54, 49], 3, 4]', [[96, 103, 54, 49], 3, 4], [[103, 54, 49], 96]], ['control [[133, 23, 147], 0, 3]', [[133, 23, 147], 0, 3], 'parity-mismatch'], ['control [[81, 29, 77], 1, 3]', [[81, 29, 77], 1, 3], 'parity-mismatch'], ['control [[167, 226, 235, 175], 0, 4]', [[167, 226, 235, 175], 0, 4], 'parity-mismatch'], ['control [[196, 130, 1, 70], 1, 4]', [[196, 130, 1, 70], 1, 4], 'parity-mismatch']], [['regression [[48, 163, 83, 128, 142, 206], 4, 6]', [[48, 163, 83, 128, 142, 206], 4, 6], [[83, 128, 142, 206, 48], 163]], ['regression [[null, 163, 83, 128, 142, 206], 4, 6]', [[None, 163, 83, 128, 142, 206], 4, 6], [[83, 128, 142, 206, 48], 163]], ['partial-repair [[45, 55, 172, 192, 118], 2, 5]', [[45, 55, 172, 192, 118], 2, 5], [[192, 118, 45, 55], 172]], ['partial-repair [[45, 55, null, 192, 118], 2, 5]', [[45, 55, None, 192, 118], 2, 5], [[192, 118, 45, 55], 172]], ['control [[97, 103, 54, 49], 3, 4]', [[97, 103, 54, 49], 3, 4], 'parity-mismatch'], ['control [[44, 55, 172, 192, 118], 2, 5]', [[44, 55, 172, 192, 118], 2, 5], 'parity-mismatch'], ['control [[94, 206, 36, 83, 230], 6, 5]', [[94, 206, 36, 83, 230], 6, 5], 'parity-mismatch'], ['control [[49, 163, 83, 128, 142, 206], 4, 6]', [[49, 163, 83, 128, 142, 206], 4, 6], 'parity-mismatch']]]
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 [[132, 23, 147], 0, 3][[132, 23], 147][[132, 23], 147]Passed
regression [[132, null, 147], 0, 3][[132, 23], 147][[132, 23], 147]Passed
control [[133, 23, 147], 0, 3]parity-mismatchparity-mismatchPassed
control [[81, 29, 77], 1, 3]parity-mismatchparity-mismatchPassed
control [[167, 226, 235, 175], 0, 4]parity-mismatchparity-mismatchPassed
control [[196, 130, 1, 70], 1, 4]parity-mismatchparity-mismatchPassed
control [[203, 146, 183, 239], 2, 4]parity-mismatchparity-mismatchPassed
control [[97, 103, 54, 49], 3, 4]parity-mismatchparity-mismatchPassed

SHA-256 / e3707bb16a1814f9cf43198af07fc10e5dadd627fc5ce51b0e6e7cd225e3d516

Verification & scope

A deterministic, bounded teaching model of the named code under the stated contract; not a production codec. 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:34.802864+00:00.

Case digest / e20ad7dd798d202c8e4c6392b5cbcaef3fb91d98659a18dac96f109a987127d8