FAILURE MAP
← Case archive

FA-29551 / HTTP retries / Open access

Late response fencing between retry attempts: Retry after logical cancellation is reported as a transport lifecycle result · case 01

Retry after logical cancellation is reported as a transport lifecycle result

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

ROOT CAUSE

Retry after logical cancellation is reported as a transport lifecycle result

VERIFIED REPAIR

Restore the specified transition if cancelled: out.append('cancelled'); continue generation.

Unsuccessful approach: The attempted repair changes the faulty site to if cancelled: out.append('stale'); continue generation but still violates a regression oracle.

Case contract

start allocates a strictly increasing generation. complete(generation,value) publishes only the current active generation and terminates it; duplicate/stale results are rejected. timeout(generation) deactivates only that generation. cancel terminates the logical request permanently. Return accepted/rejected results plus current generation and cancellation flag.

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):
    generation=0; active=False; cancelled=False; out=[]
    for e in events:
        if e[0]=='start':
            if cancelled: out.append('started'); continue
            generation+=1
            active=True
            out.append(['started',generation])
        elif e[0]=='timeout':
            if e[1]==generation: active=False
        elif e[0]=='cancel':
            cancelled=True
            active=False
        elif e[0]=='complete':
            if cancelled: out.append('cancelled'); continue
            if e[1]!=generation or not active:
                out.append('stale'); continue
            out.append(['accepted',e[2]])
            active=False
    return [out,generation,cancelled]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [[],0,False])
check('1', solve([('start',),('complete',1,N),('complete',1,N+1)]), [[['started',1],['accepted',N],'stale'],1,False])
check('2', solve([('start',),('start',),('timeout',1),('complete',1,0),('complete',2,N+5)]), [[['started',1],['started',2],'stale',['accepted',N+5]],2,False])
check('3', solve([('start',),('timeout',1),('complete',1,N)]), [[['started',1],'stale'],1,False])
check('4', solve([('start',),('cancel',),('complete',1,N),('start',)]), [[['started',1],'cancelled','cancelled'],1,True])
check('5', solve([('start',),('complete',2,N),('complete',1,N+3)]), [[['started',1],'stale',['accepted',N+3]],1,False])
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[[], 0, False][[], 0, False]Passed
1[[['started', 1], ['accepted', 1], 'stale'], 1, False][[['started', 1], ['accepted', 1], 'stale'], 1, False]Passed
2[[['started', 1], ['started', 2], 'stale', ['accepted', 6]], 2, False][[['started', 1], ['started', 2], 'stale', ['accepted', 6]], 2, False]Passed
3[[['started', 1], 'stale'], 1, False][[['started', 1], 'stale'], 1, False]Passed
4[[['started', 1], 'cancelled', 'started'], 1, True][[['started', 1], 'cancelled', 'cancelled'], 1, True]Failed
5[[['started', 1], 'stale', ['accepted', 4]], 1, False][[['started', 1], 'stale', ['accepted', 4]], 1, False]Passed

SHA-256 / 1bc33b5139f3d7d4b363c7c99a320025c1e33ac591ffff528ffefe3808d8f68f

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(events):
    generation=0; active=False; cancelled=False; out=[]
    for e in events:
        if e[0]=='start':
            if cancelled: out.append('stale'); continue
            generation+=1
            active=True
            out.append(['started',generation])
        elif e[0]=='timeout':
            if e[1]==generation: active=False
        elif e[0]=='cancel':
            cancelled=True
            active=False
        elif e[0]=='complete':
            if cancelled: out.append('cancelled'); continue
            if e[1]!=generation or not active:
                out.append('stale'); continue
            out.append(['accepted',e[2]])
            active=False
    return [out,generation,cancelled]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [[],0,False])
check('1', solve([('start',),('complete',1,N),('complete',1,N+1)]), [[['started',1],['accepted',N],'stale'],1,False])
check('2', solve([('start',),('start',),('timeout',1),('complete',1,0),('complete',2,N+5)]), [[['started',1],['started',2],'stale',['accepted',N+5]],2,False])
check('3', solve([('start',),('timeout',1),('complete',1,N)]), [[['started',1],'stale'],1,False])
check('4', solve([('start',),('cancel',),('complete',1,N),('start',)]), [[['started',1],'cancelled','cancelled'],1,True])
check('5', solve([('start',),('complete',2,N),('complete',1,N+3)]), [[['started',1],'stale',['accepted',N+3]],1,False])
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[[], 0, False][[], 0, False]Passed
1[[['started', 1], ['accepted', 1], 'stale'], 1, False][[['started', 1], ['accepted', 1], 'stale'], 1, False]Passed
2[[['started', 1], ['started', 2], 'stale', ['accepted', 6]], 2, False][[['started', 1], ['started', 2], 'stale', ['accepted', 6]], 2, False]Passed
3[[['started', 1], 'stale'], 1, False][[['started', 1], 'stale'], 1, False]Passed
4[[['started', 1], 'cancelled', 'stale'], 1, True][[['started', 1], 'cancelled', 'cancelled'], 1, True]Failed
5[[['started', 1], 'stale', ['accepted', 4]], 1, False][[['started', 1], 'stale', ['accepted', 4]], 1, False]Passed

SHA-256 / 0b898176fe2ec9f02ee046dc7aaffe5fb3dc9e93911bd886e02c9ed0aa169b50

3 / The verified repair

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

N = 1
observations = []
def solve(events):
    generation=0; active=False; cancelled=False; out=[]
    for e in events:
        if e[0]=='start':
            if cancelled: out.append('cancelled'); continue
            generation+=1
            active=True
            out.append(['started',generation])
        elif e[0]=='timeout':
            if e[1]==generation: active=False
        elif e[0]=='cancel':
            cancelled=True
            active=False
        elif e[0]=='complete':
            if cancelled: out.append('cancelled'); continue
            if e[1]!=generation or not active:
                out.append('stale'); continue
            out.append(['accepted',e[2]])
            active=False
    return [out,generation,cancelled]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [[],0,False])
check('1', solve([('start',),('complete',1,N),('complete',1,N+1)]), [[['started',1],['accepted',N],'stale'],1,False])
check('2', solve([('start',),('start',),('timeout',1),('complete',1,0),('complete',2,N+5)]), [[['started',1],['started',2],'stale',['accepted',N+5]],2,False])
check('3', solve([('start',),('timeout',1),('complete',1,N)]), [[['started',1],'stale'],1,False])
check('4', solve([('start',),('cancel',),('complete',1,N),('start',)]), [[['started',1],'cancelled','cancelled'],1,True])
check('5', solve([('start',),('complete',2,N),('complete',1,N+3)]), [[['started',1],'stale',['accepted',N+3]],1,False])
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[[], 0, False][[], 0, False]Passed
1[[['started', 1], ['accepted', 1], 'stale'], 1, False][[['started', 1], ['accepted', 1], 'stale'], 1, False]Passed
2[[['started', 1], ['started', 2], 'stale', ['accepted', 6]], 2, False][[['started', 1], ['started', 2], 'stale', ['accepted', 6]], 2, False]Passed
3[[['started', 1], 'stale'], 1, False][[['started', 1], 'stale'], 1, False]Passed
4[[['started', 1], 'cancelled', 'cancelled'], 1, True][[['started', 1], 'cancelled', 'cancelled'], 1, True]Passed
5[[['started', 1], 'stale', ['accepted', 4]], 1, False][[['started', 1], 'stale', ['accepted', 4]], 1, False]Passed

SHA-256 / 8e8a2bf9e28bc971355127f54fa2c3ceb07446320764b1c5ecca727355503ee9

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

Case digest / abc20d651c9e2f8fc27c5eb6644eed41356fd952a59dc6f5b90d51bfeb7b32d0