FAILURE MAP
← Case archive

FA-30416 / HTTP retries / Open access

Logical request deadline across retry phases: Wake state violates retry transition semantics · case 01

Wake state violates retry transition semantics

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

ROOT CAUSE

Wake state violates retry transition semantics

VERIFIED REPAIR

Restore the specified transition if state!='queued': out.append('blocked'); continue.

Unsuccessful approach: The attempted repair changes the faulty site to if state!='queued': out.append('started'); continue but still violates a regression oracle.

Case contract

begin(now,budget) pins a logical deadline. advance(time) uses monotonic logical time. queue(delay) schedules retry only for live request, wake starts only after due and strictly before deadline, complete succeeds only for running attempt before deadline. finish/cancel are terminal. Admission at expired time reports expired and closes request; queue never extends deadline. Return scheduling/attempt dispositions.

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; deadline=0; due=None; state='idle'; out=[]
    for e in events:
        if e[0]=='begin':
            now=e[1]; deadline=now+e[2]; due=None; state='ready'
        elif e[0]=='advance': now=max(now,e[1])
        elif e[0]=='cancel': state='cancelled'; due=None
        elif e[0]=='queue':
            if state not in ('ready','failed'): out.append('blocked'); continue
            if now>=deadline: state='expired'; out.append('expired'); continue
            due=now+e[1]; state='queued'; out.append(['queued',due])
        elif e[0]=='wake':
            if state!='queued': out.append('early'); continue
            if now>=deadline: state='expired'; due=None; out.append('expired'); continue
            if now<due: out.append('early'); continue
            state='running'; due=None; out.append('started')
        elif e[0]=='fail':
            if state=='running': state='failed'
        elif e[0]=='complete':
            if state!='running': out.append('blocked'); continue
            if now>=deadline: state='expired'; out.append('expired'); continue
            state='done'; out.append('success')
    return [out,deadline,state]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [[],0,'idle'])
check('1', solve([('begin',N,5),('queue',2),('wake',),('advance',N+2),('wake',),('complete',),('queue',0)]), [[['queued',N+2],'early','started','success','blocked'],N+5,'done'])
check('2', solve([('begin',N,2),('queue',2),('advance',N+2),('wake',)]), [[['queued',N+2],'expired'],N+2,'expired'])
check('3', solve([('begin',N,2),('advance',N+2),('queue',0)]), [['expired'],N+2,'expired'])
check('4', solve([('begin',N,2),('queue',0),('wake',),('advance',N+2),('complete',)]), [[['queued',N],'started','expired'],N+2,'expired'])
check('5', solve([('begin',N,5),('queue',0),('wake',),('fail',),('queue',1),('advance',N+1),('wake',),('cancel',),('queue',0)]), [[['queued',N],'started',['queued',N+1],'started','blocked'],N+5,'cancelled'])
check('6', solve([('begin',N,2),('advance',N+3),('advance',N),('queue',0)]), [['expired'],N+2,'expired'])
check('7', solve([('begin',N,3),('cancel',),('fail',),('queue',0)]), [['blocked'],N+3,'cancelled'])
check('8', solve([('begin',N,5),('queue',0),('wake',),('fail',)]), [[['queued',N],'started'],N+5,'failed'])
check('9', solve([('begin',N,5),('cancel',),('wake',)]), [['blocked'],N+5,'cancelled'])
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, 'idle'][[], 0, 'idle']Passed
1[[['queued', 3], 'early', 'started', 'success', 'blocked'], 6, 'done'][[['queued', 3], 'early', 'started', 'success', 'blocked'], 6, 'done']Passed
2[[['queued', 3], 'expired'], 3, 'expired'][[['queued', 3], 'expired'], 3, 'expired']Passed
3[['expired'], 3, 'expired'][['expired'], 3, 'expired']Passed
4[[['queued', 1], 'started', 'expired'], 3, 'expired'][[['queued', 1], 'started', 'expired'], 3, 'expired']Passed
5[[['queued', 1], 'started', ['queued', 2], 'started', 'blocked'], 6, 'cancelled'][[['queued', 1], 'started', ['queued', 2], 'started', 'blocked'], 6, 'cancelled']Passed
6[['expired'], 3, 'expired'][['expired'], 3, 'expired']Passed
7[['blocked'], 4, 'cancelled'][['blocked'], 4, 'cancelled']Passed
8[[['queued', 1], 'started'], 6, 'failed'][[['queued', 1], 'started'], 6, 'failed']Passed
9[['early'], 6, 'cancelled'][['blocked'], 6, 'cancelled']Failed

SHA-256 / a83308745053cb69ce3bb5ead4408af7e91f01076016feb432d68a01bd0ecb63

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(events):
    now=0; deadline=0; due=None; state='idle'; out=[]
    for e in events:
        if e[0]=='begin':
            now=e[1]; deadline=now+e[2]; due=None; state='ready'
        elif e[0]=='advance': now=max(now,e[1])
        elif e[0]=='cancel': state='cancelled'; due=None
        elif e[0]=='queue':
            if state not in ('ready','failed'): out.append('blocked'); continue
            if now>=deadline: state='expired'; out.append('expired'); continue
            due=now+e[1]; state='queued'; out.append(['queued',due])
        elif e[0]=='wake':
            if state!='queued': out.append('started'); continue
            if now>=deadline: state='expired'; due=None; out.append('expired'); continue
            if now<due: out.append('early'); continue
            state='running'; due=None; out.append('started')
        elif e[0]=='fail':
            if state=='running': state='failed'
        elif e[0]=='complete':
            if state!='running': out.append('blocked'); continue
            if now>=deadline: state='expired'; out.append('expired'); continue
            state='done'; out.append('success')
    return [out,deadline,state]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [[],0,'idle'])
check('1', solve([('begin',N,5),('queue',2),('wake',),('advance',N+2),('wake',),('complete',),('queue',0)]), [[['queued',N+2],'early','started','success','blocked'],N+5,'done'])
check('2', solve([('begin',N,2),('queue',2),('advance',N+2),('wake',)]), [[['queued',N+2],'expired'],N+2,'expired'])
check('3', solve([('begin',N,2),('advance',N+2),('queue',0)]), [['expired'],N+2,'expired'])
check('4', solve([('begin',N,2),('queue',0),('wake',),('advance',N+2),('complete',)]), [[['queued',N],'started','expired'],N+2,'expired'])
check('5', solve([('begin',N,5),('queue',0),('wake',),('fail',),('queue',1),('advance',N+1),('wake',),('cancel',),('queue',0)]), [[['queued',N],'started',['queued',N+1],'started','blocked'],N+5,'cancelled'])
check('6', solve([('begin',N,2),('advance',N+3),('advance',N),('queue',0)]), [['expired'],N+2,'expired'])
check('7', solve([('begin',N,3),('cancel',),('fail',),('queue',0)]), [['blocked'],N+3,'cancelled'])
check('8', solve([('begin',N,5),('queue',0),('wake',),('fail',)]), [[['queued',N],'started'],N+5,'failed'])
check('9', solve([('begin',N,5),('cancel',),('wake',)]), [['blocked'],N+5,'cancelled'])
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, 'idle'][[], 0, 'idle']Passed
1[[['queued', 3], 'early', 'started', 'success', 'blocked'], 6, 'done'][[['queued', 3], 'early', 'started', 'success', 'blocked'], 6, 'done']Passed
2[[['queued', 3], 'expired'], 3, 'expired'][[['queued', 3], 'expired'], 3, 'expired']Passed
3[['expired'], 3, 'expired'][['expired'], 3, 'expired']Passed
4[[['queued', 1], 'started', 'expired'], 3, 'expired'][[['queued', 1], 'started', 'expired'], 3, 'expired']Passed
5[[['queued', 1], 'started', ['queued', 2], 'started', 'blocked'], 6, 'cancelled'][[['queued', 1], 'started', ['queued', 2], 'started', 'blocked'], 6, 'cancelled']Passed
6[['expired'], 3, 'expired'][['expired'], 3, 'expired']Passed
7[['blocked'], 4, 'cancelled'][['blocked'], 4, 'cancelled']Passed
8[[['queued', 1], 'started'], 6, 'failed'][[['queued', 1], 'started'], 6, 'failed']Passed
9[['started'], 6, 'cancelled'][['blocked'], 6, 'cancelled']Failed

SHA-256 / 181d79adf5e0b14d8b1a85d060370561ab46f153eea16bc430d2573db35557a8

3 / The verified repair

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

N = 1
observations = []
def solve(events):
    now=0; deadline=0; due=None; state='idle'; out=[]
    for e in events:
        if e[0]=='begin':
            now=e[1]; deadline=now+e[2]; due=None; state='ready'
        elif e[0]=='advance': now=max(now,e[1])
        elif e[0]=='cancel': state='cancelled'; due=None
        elif e[0]=='queue':
            if state not in ('ready','failed'): out.append('blocked'); continue
            if now>=deadline: state='expired'; out.append('expired'); continue
            due=now+e[1]; state='queued'; out.append(['queued',due])
        elif e[0]=='wake':
            if state!='queued': out.append('blocked'); continue
            if now>=deadline: state='expired'; due=None; out.append('expired'); continue
            if now<due: out.append('early'); continue
            state='running'; due=None; out.append('started')
        elif e[0]=='fail':
            if state=='running': state='failed'
        elif e[0]=='complete':
            if state!='running': out.append('blocked'); continue
            if now>=deadline: state='expired'; out.append('expired'); continue
            state='done'; out.append('success')
    return [out,deadline,state]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
check('0', solve([]), [[],0,'idle'])
check('1', solve([('begin',N,5),('queue',2),('wake',),('advance',N+2),('wake',),('complete',),('queue',0)]), [[['queued',N+2],'early','started','success','blocked'],N+5,'done'])
check('2', solve([('begin',N,2),('queue',2),('advance',N+2),('wake',)]), [[['queued',N+2],'expired'],N+2,'expired'])
check('3', solve([('begin',N,2),('advance',N+2),('queue',0)]), [['expired'],N+2,'expired'])
check('4', solve([('begin',N,2),('queue',0),('wake',),('advance',N+2),('complete',)]), [[['queued',N],'started','expired'],N+2,'expired'])
check('5', solve([('begin',N,5),('queue',0),('wake',),('fail',),('queue',1),('advance',N+1),('wake',),('cancel',),('queue',0)]), [[['queued',N],'started',['queued',N+1],'started','blocked'],N+5,'cancelled'])
check('6', solve([('begin',N,2),('advance',N+3),('advance',N),('queue',0)]), [['expired'],N+2,'expired'])
check('7', solve([('begin',N,3),('cancel',),('fail',),('queue',0)]), [['blocked'],N+3,'cancelled'])
check('8', solve([('begin',N,5),('queue',0),('wake',),('fail',)]), [[['queued',N],'started'],N+5,'failed'])
check('9', solve([('begin',N,5),('cancel',),('wake',)]), [['blocked'],N+5,'cancelled'])
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, 'idle'][[], 0, 'idle']Passed
1[[['queued', 3], 'early', 'started', 'success', 'blocked'], 6, 'done'][[['queued', 3], 'early', 'started', 'success', 'blocked'], 6, 'done']Passed
2[[['queued', 3], 'expired'], 3, 'expired'][[['queued', 3], 'expired'], 3, 'expired']Passed
3[['expired'], 3, 'expired'][['expired'], 3, 'expired']Passed
4[[['queued', 1], 'started', 'expired'], 3, 'expired'][[['queued', 1], 'started', 'expired'], 3, 'expired']Passed
5[[['queued', 1], 'started', ['queued', 2], 'started', 'blocked'], 6, 'cancelled'][[['queued', 1], 'started', ['queued', 2], 'started', 'blocked'], 6, 'cancelled']Passed
6[['expired'], 3, 'expired'][['expired'], 3, 'expired']Passed
7[['blocked'], 4, 'cancelled'][['blocked'], 4, 'cancelled']Passed
8[[['queued', 1], 'started'], 6, 'failed'][[['queued', 1], 'started'], 6, 'failed']Passed
9[['blocked'], 6, 'cancelled'][['blocked'], 6, 'cancelled']Passed

SHA-256 / e6f1aa764c2dfd68929b23b7f3e64ecda5d21dbc3576297149619285c013b3b0

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

Case digest / 9c05e6b00f62318cdf2475df6d61958ae36e92cd4ea37d57876cd50a63062249