FAILURE MAP
← Case archive

FA-30021 / HTTP retries / Open access

Idempotency receipt expiry during retry: Monotonic time violates retry transition semantics · case 01

Monotonic time violates retry transition semantics

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

ROOT CAUSE

Monotonic time violates retry transition semantics

VERIFIED REPAIR

Restore the specified transition now=max(now,e[1]).

Unsuccessful approach: The attempted repair changes the faulty site to now=min(now,e[1]) but still violates a regression oracle.

Case contract

put(key,body,expires) creates a pending receipt if no live receipt exists at current logical time. Existing live matching key returns pending or cached; mismatched body returns conflict. commit succeeds only for a live pending receipt. at(time) moves monotonic time forward. Expiry at now is expired. lookup(key) reports missing/pending/cached. Return outcomes. Commit appends committed or ignored to expose whether a durable receipt write occurred.

Why this case matters

Offline deterministic model of HTTP request retries.

1 / The failure

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

N = 1
observations = []
def solve(events):
    now=0; records={}; out=[]
    for e in events:
        if e[0]=='at': now=e[1]
        elif e[0]=='put':
            k,body,expires=e[1:]
            old=records.get(k)
            if old is not None and old[1]<=now:
                records.pop(k); old=None
            if old is None:
                if expires<=now: out.append('expired'); continue
                records[k]=[body,expires,False,None]; out.append('new')
            elif old[0]!=body: out.append('conflict')
            elif old[2]: out.append(['cached',old[3]])
            else: out.append('pending')
        elif e[0]=='commit':
            old=records.get(e[1])
            if old is not None and old[1]>now and not old[2]:
                old[2]=True; old[3]=e[2]; out.append('committed')
            else: out.append('ignored')
        elif e[0]=='lookup':
            old=records.get(e[1])
            if old is None or old[1]<=now: out.append('missing')
            elif old[2]: out.append(['cached',old[3]])
            else: out.append('pending')
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [])
check('1', solve([('lookup','x'),('put','a',N,3),('put','a',N,3),('commit','a',N+9),('commit','a',999),('lookup','a'),('put','a',N,3),('put','a',N+1,3)]), ['missing','new','pending','committed','ignored',['cached',N+9],['cached',N+9],'conflict'])
check('2', solve([('at',3),('put','a',N,3),('put','b',N,2)]), ['expired','expired'])
check('3', solve([('put','a',N,3),('at',3),('commit','a',99),('lookup','a'),('put','a',N+1,5),('lookup','a')]), ['new','ignored','missing','new','pending'])
check('4', solve([('at',4),('put','a',N,6),('at',6),('lookup','a')]), ['new','missing'])
check('5', solve([('put','a',N,2),('at',3),('at',1),('lookup','a')]), ['new','missing'])
check('6', solve([('commit','absent',N),('lookup','absent')]), ['ignored','missing'])
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
0[][]Passed
1['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict']['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict']Passed
2['expired', 'expired']['expired', 'expired']Passed
3['new', 'ignored', 'missing', 'new', 'pending']['new', 'ignored', 'missing', 'new', 'pending']Passed
4['new', 'missing']['new', 'missing']Passed
5['new', 'pending']['new', 'missing']Failed
6['ignored', 'missing']['ignored', 'missing']Passed

SHA-256 / 3f0bcdb9b4b2ae002551e1b6ea60f3d2fee68ac435c93ce5b16751d0ad813351

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(events):
    now=0; records={}; out=[]
    for e in events:
        if e[0]=='at': now=min(now,e[1])
        elif e[0]=='put':
            k,body,expires=e[1:]
            old=records.get(k)
            if old is not None and old[1]<=now:
                records.pop(k); old=None
            if old is None:
                if expires<=now: out.append('expired'); continue
                records[k]=[body,expires,False,None]; out.append('new')
            elif old[0]!=body: out.append('conflict')
            elif old[2]: out.append(['cached',old[3]])
            else: out.append('pending')
        elif e[0]=='commit':
            old=records.get(e[1])
            if old is not None and old[1]>now and not old[2]:
                old[2]=True; old[3]=e[2]; out.append('committed')
            else: out.append('ignored')
        elif e[0]=='lookup':
            old=records.get(e[1])
            if old is None or old[1]<=now: out.append('missing')
            elif old[2]: out.append(['cached',old[3]])
            else: out.append('pending')
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [])
check('1', solve([('lookup','x'),('put','a',N,3),('put','a',N,3),('commit','a',N+9),('commit','a',999),('lookup','a'),('put','a',N,3),('put','a',N+1,3)]), ['missing','new','pending','committed','ignored',['cached',N+9],['cached',N+9],'conflict'])
check('2', solve([('at',3),('put','a',N,3),('put','b',N,2)]), ['expired','expired'])
check('3', solve([('put','a',N,3),('at',3),('commit','a',99),('lookup','a'),('put','a',N+1,5),('lookup','a')]), ['new','ignored','missing','new','pending'])
check('4', solve([('at',4),('put','a',N,6),('at',6),('lookup','a')]), ['new','missing'])
check('5', solve([('put','a',N,2),('at',3),('at',1),('lookup','a')]), ['new','missing'])
check('6', solve([('commit','absent',N),('lookup','absent')]), ['ignored','missing'])
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
0[][]Passed
1['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict']['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict']Passed
2['new', 'new']['expired', 'expired']Failed
3['new', 'committed', ['cached', 99], 'conflict', ['cached', 99]]['new', 'ignored', 'missing', 'new', 'pending']Failed
4['new', 'pending']['new', 'missing']Failed
5['new', 'pending']['new', 'missing']Failed
6['ignored', 'missing']['ignored', 'missing']Passed

SHA-256 / b73fbdffad3d1e6691629686bc696c7a9c06fe3caff5e8462225d18b40e96f14

3 / The verified repair

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

N = 1
observations = []
def solve(events):
    now=0; records={}; out=[]
    for e in events:
        if e[0]=='at': now=max(now,e[1])
        elif e[0]=='put':
            k,body,expires=e[1:]
            old=records.get(k)
            if old is not None and old[1]<=now:
                records.pop(k); old=None
            if old is None:
                if expires<=now: out.append('expired'); continue
                records[k]=[body,expires,False,None]; out.append('new')
            elif old[0]!=body: out.append('conflict')
            elif old[2]: out.append(['cached',old[3]])
            else: out.append('pending')
        elif e[0]=='commit':
            old=records.get(e[1])
            if old is not None and old[1]>now and not old[2]:
                old[2]=True; old[3]=e[2]; out.append('committed')
            else: out.append('ignored')
        elif e[0]=='lookup':
            old=records.get(e[1])
            if old is None or old[1]<=now: out.append('missing')
            elif old[2]: out.append(['cached',old[3]])
            else: out.append('pending')
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [])
check('1', solve([('lookup','x'),('put','a',N,3),('put','a',N,3),('commit','a',N+9),('commit','a',999),('lookup','a'),('put','a',N,3),('put','a',N+1,3)]), ['missing','new','pending','committed','ignored',['cached',N+9],['cached',N+9],'conflict'])
check('2', solve([('at',3),('put','a',N,3),('put','b',N,2)]), ['expired','expired'])
check('3', solve([('put','a',N,3),('at',3),('commit','a',99),('lookup','a'),('put','a',N+1,5),('lookup','a')]), ['new','ignored','missing','new','pending'])
check('4', solve([('at',4),('put','a',N,6),('at',6),('lookup','a')]), ['new','missing'])
check('5', solve([('put','a',N,2),('at',3),('at',1),('lookup','a')]), ['new','missing'])
check('6', solve([('commit','absent',N),('lookup','absent')]), ['ignored','missing'])
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
0[][]Passed
1['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict']['missing', 'new', 'pending', 'committed', 'ignored', ['cached', 10], ['cached', 10], 'conflict']Passed
2['expired', 'expired']['expired', 'expired']Passed
3['new', 'ignored', 'missing', 'new', 'pending']['new', 'ignored', 'missing', 'new', 'pending']Passed
4['new', 'missing']['new', 'missing']Passed
5['new', 'missing']['new', 'missing']Passed
6['ignored', 'missing']['ignored', 'missing']Passed

SHA-256 / aba0cac156c38bf6144172a6872debb1334951fa4c0faca744649e01ee90e8bb

Verification & scope

Stipulated bounded simulator, not a complete HTTP implementation or a standards conformance claim. 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:41:48.928266+00:00.

Case digest / 71836d5337e3c4e4ccd29ccf09206743b6595f319b08c825deed91ea447ce89c